This notebook prepares the - locational data for patents - person/firm level identifiers

Load data

condensed address data…

We are working out the exact location for a sample of firms. This exploits cheap/free usage of APIs. Specifically, the mapbox API allows 100k free calls per month. We use the exactly matched address strings to assign an approximate address to the remaining persons in the patent data. Specifically, we assign a random location of the found addresses in the same country to all addresses with country information. Note that this could be greatly refined in future work; e.g. predict the likely location use features such as sector etc. The main purpose of the exercise is now to get map points that are reasonable within specific countries.

get address data from bigquery unless already stored locally….

personfile=paste0(localbig,"\\data\\person_address.parquet")
if (!file.exists(personfile)) {
  library(bigrquery)
  library(DBI)
  
  # Set your project ID
  project_id <- "patbis"
  
  # Option 1: Direct table download
  # Specify dataset and table name
  dataset <- "inglobe"
  table <- "person_address_extract"
  
  # Download the entire table
  df <- bq_table_download(
    bq_table(project_id, dataset, table)
  )
  
  
  library(arrow)
  write_parquet(df,personfile)
}

Get person data

a little check…. are high value patents MNE ones?

#bigX=countrymap %>% inner_join(hicinno) %>% inner_join(istraxglobal)


bigX=countrymap %>% inner_join(hicinno) %>% 
   inner_join(istraxglobal) %>%
   inner_join(istraxemdeexcn) %>% 
   inner_join(istraxnational) %>% 
   rename(iso2=ctry_code) %>% 
   inner_join(country_income)%>% mutate(CNid=iso2=="CN") %>% left_join(greenid) %>% 
   mutate(anygreen=!is.na(technology))
Joining with `by = join_by(docdb_family_id)`
Joining with `by = join_by(docdb_family_id, ctry_code)`
Joining with `by = join_by(docdb_family_id, ctry_code)`
Joining with `by = join_by(docdb_family_id, ctry_code)`
Joining with `by = join_by(iso2)`
Joining with `by = join_by(docdb_family_id)`

pie charts

# Create summary
collab_summary <- bigX %>%  filter(HIC==0&CNid==0 & !(hicinno == 1 & multi == 0)) %>% 
  count(hicinno, multi) %>%
  mutate(category = case_when(
    hicinno == 1 & multi == 1 ~ "HIC & International",
    hicinno == 1 & multi == 0 ~ "HIC Only",
    hicinno == 0 & multi == 1 ~ "International Only",
    hicinno == 0 & multi == 0 ~ "No Collaboration"
  ))

# Create interactive pie chart
plot_ly(collab_summary, 
        labels = ~category, 
        values = ~n, 
        type = 'pie',
        textinfo = 'label+percent+value',
        hoverinfo = 'text',
        text = ~paste('Count:', n)) %>%
  layout(title = 'Distribution of Collaboration Types')

donut

simple regressions

model=feols(istrax_EMDEexCN*100 ~ hicinno+multi,
      bigX %>% filter(HIC==0&CNid==0))

print(model)
OLS estimation, Dep. Var.: istrax_EMDEexCN * 100
Observations: 323,210
Standard-errors: IID 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
RMSE: 46.4   Adj. R2: 0.006315

plot_collab_returns <- function(data = bigX, 
                                var, 
                                filter_HIC = TRUE,
                                filter_CNid = TRUE,
                                exclude_HIC_only = TRUE,
                                title = "Return by Collaboration Type",
                                xlab = "Spillover return per $100 invested",
                                multiply_by = 100,
                                palette = "Set1",
                                text_size = 3.5,
                                label_size = 4) {
  
  var_enquo <- enquo(var)
  
  # Apply filters conditionally
  filtered_data <- data
  if (filter_HIC) filtered_data <- filtered_data %>% filter(HIC == 0)
  if (filter_CNid) filtered_data <- filtered_data %>% filter(CNid == 0)
  if (exclude_HIC_only) filtered_data <- filtered_data %>% filter(!(hicinno == 1 & multi == 0))
  
  # Prepare summary data
  summary_data <- filtered_data %>%
    mutate(collab_type = case_when(
      hicinno == 1 & multi == 1 ~ "HIC & International",
      hicinno == 1 & multi == 0 ~ "HIC Only",
      hicinno == 0 & multi == 1 ~ "International Only",
      hicinno == 0 & multi == 0 ~ "No Collaboration"
    )) %>%
    group_by(collab_type) %>%
    summarise(
      avg_value = mean(!!var_enquo * multiply_by, na.rm = TRUE),
      n = n(),
      nn = log(n),
      .groups = "drop"
    ) %>%
    mutate(
      share = n / sum(n),
      log_share = nn / sum(nn),
      log_share_scaled = (log_share - min(log_share)) * 50,
      y_pos = row_number(),
      ymin = y_pos - log_share/2,
      ymax = y_pos + log_share/2
    ) %>%
    arrange(desc(avg_value))
  
  # Create plot
  ggplot(summary_data) +
    geom_rect(aes(xmin = 0, xmax = avg_value, ymin = ymin, ymax = ymax, fill = collab_type),
              alpha = 0.8, color = "white", linewidth = 1) +
    geom_text(aes(x = avg_value + max(avg_value)*0.02, y = y_pos, 
                  label = paste0(round(avg_value, 2))),
              hjust = 0, size = label_size, fontface = "bold", color = "black") +
    geom_text(aes(x = 0, y = y_pos, 
                  label = paste0(collab_type, "\n(n=", scales::comma(n), ", ", 
                                round(share*100, 1), "%)")),
              hjust = 1.05, size = text_size, fontface = "bold", color = "black") +
    scale_fill_brewer(palette = palette) +
    labs(title = title,
         subtitle = "Bar width proportional to log(share) of observations",
         x = xlab,
         y = NULL) +
    theme_minimal() +
    theme(
      legend.position = "none",
      panel.grid.major.y = element_blank(),
      plot.title = element_text(face = "bold", size = 14),
      axis.text.y = element_blank(),
      plot.margin = margin(10, 10, 10, 100)
    ) +
    coord_cartesian(clip = "off")
}

# Usage:
plot_collab_returns(bigX, istrax_global)

plot_collab_returns(bigX, istrax_EMDEexCN)

NA
NA
NA

find locations…

we are using tidygeocoder & mapbox…


library(arrow)
df=read_parquet(paste0(localbig,"\\data\\person_address.parquet"))

library(tidygeocoder)

# create a dataframe with addresses

## let's check if we have stored such a dataframe previously
mboxfile=paste0(localbig,"\\data\\lat_longs_mapbox.parquet")
if (!file.exists(mboxfile)) {
  #df=df %>% mutate(rr=runif(1:n()))  %>% arrange(rr) %>% select(-rr)
  some_addresses=df %>% head(10) %>% mutate(addressstr=paste(person_address,person_ctry_code,sep=","))
  # geocode the addresses
  lat_longs_mapbox <- some_addresses %>%
    geocode(addressstr, method = 'mapbox', 
            #flatten=T,
            #full_results=T,
            #id = id,
            
            #type= type,
            lat = latitude , long = longitude)
  #> Passing 3 addresses to the Nominatim single address geocoder
  #> Query completed in: 3.2 seconds
} else{
  
  lat_longs_mapbox=read_parquet(mboxfile)
  
}



recursive_mapbox=function(){
  df_filtered <- df %>%
    anti_join(lat_longs_mapbox, by = "person_id")
  
  df_filtered=df_filtered %>% mutate(rr=runif(1:n()))  %>% arrange(rr) %>% select(-rr)
  
  some_addresses=df_filtered %>% head(1500) %>%  mutate(addressstr=paste(person_address,person_ctry_code,sep=","))
  print(nrow(df_filtered))
  lat_longs_mapbox2 <- some_addresses %>%
    geocode(addressstr, method = 'mapbox', lat = latitude , long = longitude,full_results=F)
  
  
  lat_longs_mapbox_new=lat_longs_mapbox %>% bind_rows(lat_longs_mapbox2)
  #lat_longs_mapbox=lat_longs_mapbox %>% bind_rows(lat_longs_mapbox2)
  return(lat_longs_mapbox_new)
}

#lat_longs_mapbox=lat_longs_mapbox_new  %>% distinct()
#lat_longs_mapbox=lat_longs_mapbox  %>% distinct() %>% select(-rr)



lat_longs_mapbox=recursive_mapbox()
write_parquet(lat_longs_mapbox,paste0(localbig,"\\data\\lat_longs_mapbox.parquet"))

reading back in from disk

lat_longs_mapbox=read_parquet(paste0(localbig,"\\data\\lat_longs_mapbox.parquet"))

Cleaning up

We examine if the points suggested fall within the country boundaries, which doesn’t always seem the case…


test=lat_longs_mapbox %>% filter(nchar(person_address)<=2)

testdf=lat_longs_mapbox %>% filter(!is.na(person_ctry_code) & !is.na(longitude))


library(sf)
library(rnaturalearth)

# Get world boundaries
world <- ne_countries(scale = "medium", returnclass = "sf")








world <- world %>%
  mutate(iso_a2_fixed = case_when(
    name == "Taiwan" ~ "TW",
    name == "Kosovo" ~ "XK",
    iso_a2 == "-99" & !is.na(iso_a3) ~ iso_a3,  # Fallback to iso_a3
    TRUE ~ iso_a2
  ))










# Convert dataframe to spatial points
df_sf <- st_as_sf(testdf, 
                  coords = c("longitude", "latitude"), 
                  crs = 4326)

prepare progress bar

library(progress)

n <- nrow(testdf)

pb <- progress_bar$new( format = “[:bar] :percent eta: :eta”, total = n, clear = FALSE, width = 60 )

Initialize ok column

testdf$ok <- FALSE

Check each point

for(i in 1:nrow(testdf)) { country_poly <- world[world\(iso_a2 == testdf\)person_ctry_code[i], ] if(nrow(country_poly) > 0) { testdf\(ok[i] <- st_intersects(df_sf[i,], country_poly, sparse = FALSE)[1,1] } pb\)tick() }




# Detect number of cores
library(parallel)
n_cores <- detectCores() - 2  # Leave one core free

# Create cluster
cl <- makeCluster(n_cores)

# Export necessary objects to cluster
clusterExport(cl, c("df_sf", "world", "testdf"), envir = environment())
clusterEvalQ(cl, library(sf))

# Parallel processing
testdf$ok <- parSapply(cl, 1:nrow(testdf), function(i) {
  country_poly <- world[world$iso_a2 == testdf$person_ctry_code[i], ]
  if(nrow(country_poly) > 0) {
    return(st_intersects(df_sf[i,], country_poly, sparse = FALSE)[1,1])
  } else {
    return(FALSE)
  }
})


# Stop cluster
stopCluster(cl)


test=testdf %>% filter(person_ctry_code=="TW")


# correct some issues
testdf=testdf %>% mutate(ok=ifelse(person_ctry_code=="TW" ,T,ok), # correct some Chines corruption of the shapefile
                         ok=ifelse(nchar(gsub("\\s+", "", person_ctry_code))<2 ,T,ok) )

Infer iso2



library(sf)
library(rnaturalearth)
library(dplyr)

# Get world boundaries
world <- ne_countries(scale = "medium", returnclass = "sf")

# Initialize inferrediso2 column
lat_longs_mapbox <- lat_longs_mapbox %>%
  mutate(inferrediso2 = NA_character_)

# Filter to only rows with valid coordinates
valid_coords <- !is.na(lat_longs_mapbox$latitude) & 
                !is.na(lat_longs_mapbox$longitude)

if(sum(valid_coords) > 0) {
  # Convert to spatial points (only valid coordinates)
  points_sf <- st_as_sf(lat_longs_mapbox[valid_coords, ], 
                        coords = c("longitude", "latitude"), 
                        crs = 4326)
  
  # Spatial join to find which country polygon contains each point
  points_with_country <- st_join(points_sf, 
                                 world %>% select(iso_a2), 
                                 join = st_intersects,
                                 left = TRUE)
  
  # Extract inferrediso2
  lat_longs_mapbox$inferrediso2[valid_coords] <- points_with_country$iso_a2
  
  # Special handling for Taiwan
  # Define Taiwan's bounding box with some buffer
  taiwan_bounds <- list(
    lat_min = 21.8,
    lat_max = 25.4,
    lon_min = 119.3,
    lon_max = 122.1
  )
  
  # Identify points in Taiwan region
  in_taiwan_region <- valid_coords & 
                      lat_longs_mapbox$latitude >= taiwan_bounds$lat_min & 
                      lat_longs_mapbox$latitude <= taiwan_bounds$lat_max & 
                      lat_longs_mapbox$longitude >= taiwan_bounds$lon_min & 
                      lat_longs_mapbox$longitude <= taiwan_bounds$lon_max
  
  # Override with TW for points in Taiwan region (regardless of what spatial join said)
  lat_longs_mapbox$inferrediso2[in_taiwan_region] <- "TW"
}

clean up some more…


lat_longs_mapbox$inferrediso2[lat_longs_mapbox$inferrediso2=="-99"|lat_longs_mapbox$inferrediso2==""] <- NA

lat_longs_mapbox= lat_longs_mapbox %>% mutate(person_ctry_code=gsub("\\s+", "", person_ctry_code)) %>%
                                       mutate(person_ctry_code=ifelse(person_ctry_code=="" & !is.na(inferrediso2),inferrediso2,person_ctry_code)) 


test=lat_longs_mapbox %>% filter(person_ctry_code=="TW")



lat_longs_mapbox=lat_longs_mapbox %>% filter(!is.na(latitude) ) %>% 
                                      filter(!is.na(person_ctry_code)) %>% 
                                      filter(person_ctry_code!="") %>% 
                                      filter(!(person_ctry_code!=inferrediso2 & !is.na(inferrediso2) ))



lat_longs_bycountry=lat_longs_mapbox %>% group_by(person_ctry_code,longitude,latitude) %>% summarise(innos=n())

Let’s have a look:


library(leaflet)

# With clustering (much better for 100k points!)
leaflet(lat_longs_mapbox) %>%
  addTiles() %>%
  addCircleMarkers(
    lng = ~longitude,
    lat = ~latitude,
    radius = 3,
    fillOpacity = 0.5,
    stroke = FALSE,
    clusterOptions = markerClusterOptions()
  )

deal with capital cities …. gonna be used as default…



# Create a comprehensive dataframe of capital cities with coordinates
capital_coords <- data.frame(
  iso2 = c(
    "AF", "AL", "DZ", "AD", "AO", "AG", "AR", "AM", "AU", "AT",
    "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BT",
    "BO", "BA", "BW", "BR", "BN", "BG", "BF", "BI", "KH", "CM",
    "CA", "CV", "CF", "TD", "CL", "CN", "CO", "KM", "CG", "CD",
    "CR", "CI", "HR", "CU", "CY", "CZ", "DK", "DJ", "DM", "DO",
    "EC", "EG", "SV", "GQ", "ER", "EE", "ET", "FJ", "FI", "FR",
    "GA", "GM", "GE", "DE", "GH", "GR", "GD", "GT", "GN", "GW",
    "GY", "HT", "HN", "HU", "IS", "IN", "ID", "IR", "IQ", "IE",
    "IL", "IT", "JM", "JP", "JO", "KZ", "KE", "KI", "KP", "KR",
    "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY", "LI", "LT",
    "LU", "MK", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MR",
    "MU", "MX", "FM", "MD", "MC", "MN", "ME", "MA", "MZ", "MM",
    "NA", "NR", "NP", "NL", "NZ", "NI", "NE", "NG", "NO", "OM",
    "PK", "PW", "PS", "PA", "PG", "PY", "PE", "PH", "PL", "PT",
    "QA", "RO", "RU", "RW", "KN", "LC", "VC", "WS", "SM", "ST",
    "SA", "SN", "RS", "SC", "SL", "SG", "SK", "SI", "SB", "SO",
    "ZA", "SS", "ES", "LK", "SD", "SR", "SZ", "SE", "CH", "SY",
    "TW", "TJ", "TZ", "TH", "TL", "TG", "TO", "TT", "TN", "TR",
    "TM", "TV", "UG", "UA", "AE", "GB", "US", "UY", "UZ", "VU",
    "VA", "VE", "VN", "YE", "ZM", "ZW"
  ),
  capital = c(
    "Kabul", "Tirana", "Algiers", "Andorra la Vella", "Luanda", "Saint John's", "Buenos Aires", "Yerevan", "Canberra", "Vienna",
    "Baku", "Nassau", "Manama", "Dhaka", "Bridgetown", "Minsk", "Brussels", "Belmopan", "Porto-Novo", "Thimphu",
    "Sucre", "Sarajevo", "Gaborone", "Brasília", "Bandar Seri Begawan", "Sofia", "Ouagadougou", "Gitega", "Phnom Penh", "Yaoundé",
    "Ottawa", "Praia", "Bangui", "N'Djamena", "Santiago", "Beijing", "Bogotá", "Moroni", "Brazzaville", "Kinshasa",
    "San José", "Yamoussoukro", "Zagreb", "Havana", "Nicosia", "Prague", "Copenhagen", "Djibouti", "Roseau", "Santo Domingo",
    "Quito", "Cairo", "San Salvador", "Malabo", "Asmara", "Tallinn", "Addis Ababa", "Suva", "Helsinki", "Paris",
    "Libreville", "Banjul", "Tbilisi", "Berlin", "Accra", "Athens", "St. George's", "Guatemala City", "Conakry", "Bissau",
    "Georgetown", "Port-au-Prince", "Tegucigalpa", "Budapest", "Reykjavik", "New Delhi", "Jakarta", "Tehran", "Baghdad", "Dublin",
    "Jerusalem", "Rome", "Kingston", "Tokyo", "Amman", "Nur-Sultan", "Nairobi", "Tarawa", "Pyongyang", "Seoul",
    "Kuwait City", "Bishkek", "Vientiane", "Riga", "Beirut", "Maseru", "Monrovia", "Tripoli", "Vaduz", "Vilnius",
    "Luxembourg", "Skopje", "Antananarivo", "Lilongwe", "Kuala Lumpur", "Malé", "Bamako", "Valletta", "Majuro", "Nouakchott",
    "Port Louis", "Mexico City", "Palikir", "Chișinău", "Monaco", "Ulaanbaatar", "Podgorica", "Rabat", "Maputo", "Naypyidaw",
    "Windhoek", "Yaren", "Kathmandu", "Amsterdam", "Wellington", "Managua", "Niamey", "Abuja", "Oslo", "Muscat",
    "Islamabad", "Ngerulmud", "Ramallah", "Panama City", "Port Moresby", "Asunción", "Lima", "Manila", "Warsaw", "Lisbon",
    "Doha", "Bucharest", "Moscow", "Kigali", "Basseterre", "Castries", "Kingstown", "Apia", "San Marino", "São Tomé",
    "Riyadh", "Dakar", "Belgrade", "Victoria", "Freetown", "Singapore", "Bratislava", "Ljubljana", "Honiara", "Mogadishu",
    "Pretoria", "Juba", "Madrid", "Colombo", "Khartoum", "Paramaribo", "Mbabane", "Stockholm", "Bern", "Damascus",
    "Taipei", "Dushanbe", "Dodoma", "Bangkok", "Dili", "Lomé", "Nuku'alofa", "Port of Spain", "Tunis", "Ankara",
    "Ashgabat", "Funafuti", "Kampala", "Kyiv", "Abu Dhabi", "London", "Washington, D.C.", "Montevideo", "Tashkent", "Port Vila",
    "Vatican City", "Caracas", "Hanoi", "Sana'a", "Lusaka", "Harare"
  ),
  latitude = c(
    34.5553, 41.3275, 36.7538, 42.5063, -8.8383, 17.1175, -34.6037, 40.1792, -35.2809, 48.2082,
    40.4093, 25.0343, 26.0667, 23.8103, 13.0969, 53.9045, 50.8503, 17.2510, 6.4969, 27.4728,
    -19.0196, 43.8564, -24.6282, -15.8267, 4.8895, 42.6977, 12.3714, -3.3731, 11.5564, 3.8480,
    45.4215, 14.9177, 4.3947, 12.1348, -33.4489, 39.9042, 4.7110, -11.7172, -4.2634, -4.4419,
    9.9281, 6.8270, 45.8150, 23.1136, 35.1856, 50.0755, 55.6761, 11.8251, 15.3017, 18.4861,
    -0.1807, 30.0444, 13.6929, 3.7504, 15.3229, 59.4370, 9.0320, -18.1248, 60.1699, 48.8566,
    0.4162, 13.4549, 41.7151, 52.5200, 5.6037, 37.9838, 12.0561, 14.6349, 9.6412, 11.8637,
    6.8013, 18.5944, 14.0723, 47.4979, 64.1466, 28.6139, -6.2088, 35.6892, 33.3152, 53.3498,
    31.7683, 41.9028, 17.9714, 35.6762, 31.9454, 51.1694, -1.2921, 1.3382, 39.0392, 37.5665,
    29.3759, 42.8746, 17.9757, 56.9496, 33.8938, -29.3167, 6.3156, 32.8872, 47.1410, 54.6872,
    49.6116, 41.9973, -18.8792, -13.9626, 3.1390, 4.1755, 12.6392, 35.8989, 7.0897, 18.0735,
    -20.1609, 19.4326, 6.9147, 47.0105, 43.7384, 47.9186, 42.4304, 34.0209, -25.9655, 19.7633,
    -22.5597, -0.5477, 27.7172, 52.3676, -41.2865, 12.1150, 13.5127, 9.0765, 59.9139, 23.5880,
    33.6844, 7.5006, 31.9522, 8.9824, -9.4438, -25.2637, -12.0464, 14.5995, 52.2297, 38.7223,
    25.2854, 44.4268, 55.7558, -1.9403, 17.3026, 13.9094, 13.4877, -13.8333, 43.9424, 0.3364,
    24.7136, 14.7167, 44.7866, -4.6796, 8.4657, 1.3521, 48.1486, 46.0569, -9.4456, 2.0469,
    -25.7479, 4.8517, 40.4168, 6.9271, 15.5007, 5.8520, -26.3054, 59.3293, 46.9481, 33.5138,
    25.0330, 38.5598, -6.7924, 13.7563, -8.5569, 6.1256, -21.1789, 10.6918, 11.8745, 39.9334,
    37.9601, -8.5211, 0.3476, 50.4501, 24.4539, 51.5074, 38.9072, -34.9011, 41.2995, -17.7334,
    41.9029, 10.4806, 21.0285, 15.5527, -15.4167, -17.8252
  ),
  longitude = c(
    69.2075, 19.8187, 3.0588, 1.5218, 13.2344, -61.8456, -58.3816, 44.4991, 149.1300, 16.3738,
    49.8671, -77.3963, 50.5577, 90.4125, -59.6162, 27.5615, 4.3517, -88.7590, 2.6289, 89.6390,
    -65.2627, 18.4131, 25.9231, -47.9292, 114.9400, 23.3219, -1.5247, 29.8739, 104.9282, 11.5021,
    -75.6972, -23.5087, 18.5582, 15.0445, -70.6693, 116.4074, -74.0721, 43.2551, 15.2662, 15.2663,
    -84.0907, -5.2893, 15.9819, -82.3666, 33.3823, 14.4378, 12.5683, 43.1456, -61.3870, -69.9312,
    -78.4678, 31.2357, -89.2182, 8.7832, 38.9251, 24.7536, 38.7469, 178.4419, 24.9384, 2.3522,
    9.4673, -16.5790, 44.8271, 13.4050, -0.1870, 23.7275, -61.7480, -90.5069, -13.5784, -15.5989,
    -58.1551, -72.3074, -87.2068, 19.0402, -21.9426, 77.2090, 106.8456, 51.3890, 44.3661, -6.2603,
    35.2137, 12.4964, -76.7931, 139.6503, 35.9284, 71.4704, 36.8219, 172.9790, 125.7625, 126.9780,
    47.9774, 74.5698, 102.6000, 24.1052, 35.4953, 27.4974, -10.7969, 13.1913, 9.5215, 25.2797,
    6.1296, 21.4314, 47.5079, 33.7738, 101.6869, 73.5093, -8.0029, 14.5146, 171.1845, -15.9582,
    57.5012, -99.1332, 158.1611, 28.8497, 7.4246, 106.9057, 19.2594, -6.8498, 32.5732, 96.1561,
    17.0832, 166.9315, 85.3240, 4.9041, 174.7633, -86.2362, 2.1254, 7.4951, 10.7522, 58.4059,
    73.0479, 134.4893, 35.2332, -79.5199, 147.1803, -57.5759, -77.0428, 120.9842, 21.0122, -9.1393,
    51.5310, 26.1025, 37.6173, -1.9536, -62.7177, -60.9789, -61.2248, -171.7667, 12.4578, 6.7273,
    46.7160, -17.4467, 20.4489, 55.4540, -13.2317, 103.8198, 17.1077, 14.5058, 159.9729, 45.3182,
    28.1879, 31.5825, -3.7038, 79.8612, 32.5599, -55.2038, 31.1367, 18.0686, 7.4474, 36.2765,
    121.5654, 68.7870, 35.7516, 100.5018, 125.5603, 1.2313, -175.1982, -61.5089, 10.1815, 32.8597,
    58.3260, 179.1942, 32.5825, 30.5234, 54.3773, -0.1278, -77.0369, -56.1645, 69.2401, 168.3273,
    12.4534, -66.8792, 105.8542, 44.2070, 28.2871, 31.0522
  ),
  stringsAsFactors = FALSE
)

# View the dataframe
head(capital_coords, 10)

# Check that Taiwan is included
capital_coords[capital_coords$iso2 == "TW", ]

# Optional: Sort by ISO2 code
capital_coords <- capital_coords[order(capital_coords$iso2), ]

# Print summary
cat("Total countries:", nrow(capital_coords), "\n")
library(plotly)
library(ggplot2)
library(maps)

# Create ggplot
world_map <- map_data("world")

p <- ggplot() +
  geom_polygon(data = world_map, 
               aes(x = long, y = lat, group = group),
               fill = "lightgray", 
               color = "white", 
               linewidth = 0.2) +
  geom_point(data = capital_coords,
             aes(x = longitude, y = latitude, 
                 text = paste0("Capital: ", capital, "\n",
                              "Country: ", iso2, "\n",
                              "Coordinates: ", round(latitude, 2), ", ", round(longitude, 2))),
             color = "red", 
             size = 2, 
             alpha = 0.7) +
  theme_minimal() +
  labs(title = "World Capital Cities - Interactive",
       x = "Longitude",
       y = "Latitude")

# Convert to interactive plotly
ggplotly(p, tooltip = "text") %>%
  layout(
    hoverlabel = list(bgcolor = "white", font = list(size = 12))
  )

now infer long and lats from either the succesful matches or if there is none…let’s just use the capital location…

  library(bigrquery)
  library(DBI)
  
  # Set your project ID
  project_id <- "patbis"
  
  # Option 1: Direct table download
  # Specify dataset and table name
  dataset <- "inglobe"
  table <- "person_address_extract"
  

  # Upload - this creates or replaces the table
bq_table_upload(
  x = bq_table(project_id, dataset, "lat_longs_mapbox"),
  values = lat_longs_mapbox,
  #fields = df,  # Optional: specify schema
  write_disposition = "WRITE_TRUNCATE"  # Replaces existing table
)



capital_coords=capital_coords %>% mutate(person_ctry_code=iso2) %>% select(person_ctry_code,latitude,longitude)
bq_table_upload(
  x = bq_table(project_id, dataset, "capital_coords"),
  values = capital_coords,
  #fields = df,  # Optional: specify schema
  write_disposition = "WRITE_TRUNCATE"  # Replaces existing table
)

  



anti=df %>% anti_join(lat_longs_mapbox) %>% select(person_id,person_ctry_code) 
anti=anti %>% filter(!is.na(person_ctry_code) & gsub("\\s+", "", person_ctry_code)!="")





lat_long=lat_longs_mapbox %>% select(longitude,latitude,person_ctry_code) %>% bind_rows(capital_coords) %>% group_by(person_ctry_code) %>% 
  mutate(rr=runif(1:n())) %>% arrange(person_ctry_code, rr) %>% mutate(nn=1:n()) %>% filter(nn<3) %>%  # Let's restrict to 5 places per country...
  select(longitude,latitude,person_ctry_code)


anti=anti %>% left_join(lat_long,by="person_ctry_code") %>%
  group_by(person_id) %>% mutate(rr=runif(1:n())) %>% arrange(person_id, rr) %>% mutate(nn=1:n()) %>% filter(nn==1)


anti=anti %>% select(-nn,-rr)

dfnew=anti %>% bind_rows(lat_longs_mapbox) %>% select(person_id, person_ctry_code,longitude,latitude)



write_parquet(dfnew,paste0(localbig,"\\data\\dfnew.parquet"))

library(leaflet)
library(dplyr)
library(arrow)
dfnew=read_parquet(paste0(localbig,"\\data\\dfnew.parquet"))


#df_sample <- dfnew %>% slice_sample(n = 10000)



library(data.table)
dfnew <- as.data.table(dfnew)
dt_sample <- dfnew[sample(.N, 100000)]

# With clustering (much better for 100k points!)
leaflet(dt_sample) %>%
  addTiles() %>%
  addCircleMarkers(
    lng = ~longitude,
    lat = ~latitude,
    radius = 3,
    fillOpacity = 0.5,
    stroke = FALSE,
    clusterOptions = markerClusterOptions()
  )
bq_table_upload(
  x = bq_table(project_id, dataset, "dfnew"),
  values = dfnew,
  #fields = df,  # Optional: specify schema
  write_disposition = "WRITE_TRUNCATE"  # Replaces existing table
)
LS0tDQp0aXRsZTogIlByZXBhcmUgdmFyaW91cyBiaXRzIG9mIGRhdGEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIG5vdGVib29rIHByZXBhcmVzIHRoZSANCi0gbG9jYXRpb25hbCBkYXRhIGZvciBwYXRlbnRzDQotIHBlcnNvbi9maXJtIGxldmVsIGlkZW50aWZpZXJzDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KGRwbHlyKQ0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KaWYgKFN5cy5pbmZvKClbIm5vZGVuYW1lIl0gPT0gIlBDQUNMLTlSVlNRNzQiKSB7DQogIGRhdGFkPSJDOlxcVXNlcnNcXHJtYXJ0aW43XFxPbmVEcml2ZSAtIFdCR1xccHJvamVjdHNcXFBSSU5aR2xvYmFsXFxpbXBlcmlhbF9jb2xsZWdlX3Zpc3VhbGl6YXRpb25cXDAxX2RhdGEiDQogIGxvY2FsYmlnPSJDOlxcVXNlcnNcXHJtYXJ0aW43XFxPbmVEcml2ZSAtIEltcGVyaWFsIENvbGxlZ2UgTG9uZG9uXFxpbmdsb2JlIg0KfQ0KDQpgYGANCg0KDQojIExvYWQgZGF0YQ0KDQpgYGB7cn0NCg0KDQoNCg0KI3VybD0iLi4vLi4vZGF0YS9wYXRlbnRfZGF0YS9wYXRiaXMyMDIxL2RhdGEvZnJvbVdBVFNPTi9pbm5vc19jdHJ5X2luZGljYXRvcnMuZHN2Ig0KDQoNCiN1cmw9Ii4uLy4uL2RhdGEvcGF0ZW50X2RhdGEvcGF0YmlzMjAyMS9kYXRhL2Zyb21XQVRTT04vaW5ub3NfaXN0cmF4ZmllbGRfbmF0aW9uYWxrZXlfMjAwOV8yMDE4LmRzdiINCg0KI2NvdW50cnltYXAgPC0gcmVhZF90c3YodXJsKQ0KDQpjaXRlcz1yZWFkX3RzdihwYXN0ZTAoZGF0YWQsIlxcY2l0ZW5ldF9ub3NlbGYiKSkNCg0KDQojY2FwaXRhbGNpdGllcz1yZWFkLmNzdihwYXN0ZTAobG9jYWxiaWcsIlxcZGF0YVxcY291bnRyaWVzX2NvZGVzX2FuZF9jb29yZGluYXRlcy5jc3YiKSkNCg0KDQpgYGANCg0KDQoNCg0KIyMgY29uZGVuc2VkIGFkZHJlc3MgZGF0YS4uLg0KDQpXZSBhcmUgd29ya2luZyBvdXQgdGhlIGV4YWN0IGxvY2F0aW9uIGZvciBhIHNhbXBsZSBvZiBmaXJtcy4gVGhpcyBleHBsb2l0cyBjaGVhcC9mcmVlIHVzYWdlIG9mIEFQSXMuDQpTcGVjaWZpY2FsbHksIHRoZSBtYXBib3ggQVBJIGFsbG93cyAxMDBrIGZyZWUgY2FsbHMgcGVyIG1vbnRoLg0KV2UgdXNlIHRoZSBleGFjdGx5IG1hdGNoZWQgYWRkcmVzcyBzdHJpbmdzIHRvIGFzc2lnbiBhbiBhcHByb3hpbWF0ZSBhZGRyZXNzIHRvIHRoZSByZW1haW5pbmcgcGVyc29ucyBpbiB0aGUgDQpwYXRlbnQgZGF0YS4gDQpTcGVjaWZpY2FsbHksIHdlIGFzc2lnbiBhIHJhbmRvbSBsb2NhdGlvbiBvZiB0aGUgZm91bmQgYWRkcmVzc2VzIGluIHRoZSBzYW1lIGNvdW50cnkgdG8gYWxsIGFkZHJlc3NlcyB3aXRoIGNvdW50cnkgaW5mb3JtYXRpb24uDQpOb3RlIHRoYXQgdGhpcyBjb3VsZCBiZSBncmVhdGx5IHJlZmluZWQgaW4gZnV0dXJlIHdvcms7IGUuZy4gcHJlZGljdCB0aGUgbGlrZWx5IGxvY2F0aW9uIHVzZSBmZWF0dXJlcyBzdWNoIGFzIHNlY3RvciBldGMuDQpUaGUgbWFpbiBwdXJwb3NlIG9mIHRoZSBleGVyY2lzZSBpcyBub3cgdG8gZ2V0IG1hcCBwb2ludHMgdGhhdCBhcmUgcmVhc29uYWJsZSB3aXRoaW4gc3BlY2lmaWMgY291bnRyaWVzLg0KDQoNCiMjIyBnZXQgYWRkcmVzcyBkYXRhIGZyb20gYmlncXVlcnkgdW5sZXNzIGFscmVhZHkgc3RvcmVkIGxvY2FsbHkuLi4uDQoNCmBgYHtyfQ0KcGVyc29uZmlsZT1wYXN0ZTAobG9jYWxiaWcsIlxcZGF0YVxccGVyc29uX2FkZHJlc3MucGFycXVldCIpDQppZiAoIWZpbGUuZXhpc3RzKHBlcnNvbmZpbGUpKSB7DQogIGxpYnJhcnkoYmlncnF1ZXJ5KQ0KICBsaWJyYXJ5KERCSSkNCiAgDQogICMgU2V0IHlvdXIgcHJvamVjdCBJRA0KICBwcm9qZWN0X2lkIDwtICJwYXRiaXMiDQogIA0KICAjIE9wdGlvbiAxOiBEaXJlY3QgdGFibGUgZG93bmxvYWQNCiAgIyBTcGVjaWZ5IGRhdGFzZXQgYW5kIHRhYmxlIG5hbWUNCiAgZGF0YXNldCA8LSAiaW5nbG9iZSINCiAgdGFibGUgPC0gInBlcnNvbl9hZGRyZXNzX2V4dHJhY3QiDQogIA0KICAjIERvd25sb2FkIHRoZSBlbnRpcmUgdGFibGUNCiAgZGYgPC0gYnFfdGFibGVfZG93bmxvYWQoDQogICAgYnFfdGFibGUocHJvamVjdF9pZCwgZGF0YXNldCwgdGFibGUpDQogICkNCiAgDQogIA0KICBsaWJyYXJ5KGFycm93KQ0KICB3cml0ZV9wYXJxdWV0KGRmLHBlcnNvbmZpbGUpDQp9DQpgYGANCg0KDQoNCiMjIEdldCBwZXJzb24gZGF0YQ0KDQpgYGB7cn0NCiMjIEludmVudG9ycw0KaW52ZW50b3JmaWxlPXBhc3RlMChsb2NhbGJpZywiXFxkYXRhXFxpbnZlbnRvcnMucGFycXVldCIpDQppZiAoIWZpbGUuZXhpc3RzKGludmVudG9yZmlsZSkpIHsNCiAgbGlicmFyeShiaWdycXVlcnkpDQogIGxpYnJhcnkoREJJKQ0KICANCiAgIyBTZXQgeW91ciBwcm9qZWN0IElEDQogIHByb2plY3RfaWQgPC0gInBhdGJpcyINCiAgDQogICMgT3B0aW9uIDE6IERpcmVjdCB0YWJsZSBkb3dubG9hZA0KICAjIFNwZWNpZnkgZGF0YXNldCBhbmQgdGFibGUgbmFtZQ0KICBkYXRhc2V0IDwtICJpbmdsb2JlIg0KICB0YWJsZSA8LSAiaW52ZW50b3JzIg0KICANCiAgIyBEb3dubG9hZCB0aGUgZW50aXJlIHRhYmxlDQogIGludmVudG9ycyA8LSBicV90YWJsZV9kb3dubG9hZCgNCiAgICBicV90YWJsZShwcm9qZWN0X2lkLCBkYXRhc2V0LCB0YWJsZSkNCiAgKQ0KICANCiAgDQogIGxpYnJhcnkoYXJyb3cpDQogIHdyaXRlX3BhcnF1ZXQoaW52ZW50b3JzLGludmVudG9yZmlsZSkNCn0NCg0KDQoNCg0KIyBTZXQgeW91ciBwcm9qZWN0IElEDQpwcm9qZWN0X2lkIDwtICJwYXRiaXMiDQpkYXRhc2V0IDwtICJpbmdsb2JlIg0KDQojIERlZmluZSBsaXN0IG9mIHRhYmxlcyB0byBkb3dubG9hZA0KdGFibGVzX3RvX2Rvd25sb2FkIDwtIGMoImludmVudG9ycyIsICJob2xkZXJzIiwgICJpbnZlbnRvcl9jb3VudHJpZXMiLCAiaG9sZGVyX2NvdW50cmllcyIpDQojdGFibGVzX3RvX2Rvd25sb2FkIDwtIGMoICJpbnZlbnRvcl9jb3VudHJpZXMiLCAiaG9sZGVyX2NvdW50cmllcyIpDQojIExvb3AgdGhyb3VnaCBlYWNoIHRhYmxlDQpmb3IgKHRhYmxlIGluIHRhYmxlc190b19kb3dubG9hZCkgew0KICAjIENyZWF0ZSBsb2NhbCBmaWxlbmFtZQ0KICBsb2NhbF9maWxlIDwtIHBhc3RlMChsb2NhbGJpZywgIlxcZGF0YVxcIiwgdGFibGUsICIucGFycXVldCIpDQogIA0KICAjIENoZWNrIGlmIGZpbGUgYWxyZWFkeSBleGlzdHMNCiAgaWYgKCFmaWxlLmV4aXN0cyhsb2NhbF9maWxlKSkgew0KICAgIG1lc3NhZ2UoIkRvd25sb2FkaW5nICIsIHRhYmxlLCAiLi4uIikNCiAgICANCiAgICAjIERvd25sb2FkIHRoZSB0YWJsZQ0KICAgIHRhYmxlX2RhdGEgPC0gYnFfdGFibGVfZG93bmxvYWQoDQogICAgICBicV90YWJsZShwcm9qZWN0X2lkLCBkYXRhc2V0LCB0YWJsZSkNCiAgICApDQogICAgDQogICAgIyBTYXZlIGFzIHBhcnF1ZXQNCiAgICB3cml0ZV9wYXJxdWV0KHRhYmxlX2RhdGEsIGxvY2FsX2ZpbGUpDQogICAgbWVzc2FnZSgiU2F2ZWQgdG8gIiwgbG9jYWxfZmlsZSkNCiAgfSBlbHNlIHsNCiAgICBtZXNzYWdlKCJGaWxlIGFscmVhZHkgZXhpc3RzOiAiLCBsb2NhbF9maWxlKQ0KICB9DQp9DQoNCg0KYGBgDQoNCiMjIyBhIGxpdHRsZSBjaGVjay4uLi4gYXJlIGhpZ2ggdmFsdWUgcGF0ZW50cyBNTkUgb25lcz8NCg0KYGBge3J9DQoNCiMgRGVmaW5lIGxpc3Qgb2YgdGFibGVzIHRvIHJlYWQNCnRhYmxlc190b19yZWFkIDwtIGMoICJpbnZlbnRvcl9jb3VudHJpZXMiLCAiaG9sZGVyX2NvdW50cmllcyIpDQoNCiMgTG9vcCB0aHJvdWdoIGVhY2ggdGFibGUgYW5kIHJlYWQgaW50byBhIGRhdGFmcmFtZQ0KZm9yICh0YWJsZSBpbiB0YWJsZXNfdG9fcmVhZCkgew0KICAjIENyZWF0ZSBmaWxlbmFtZQ0KICBsb2NhbF9maWxlIDwtIHBhc3RlMChsb2NhbGJpZywgIlxcZGF0YVxcIiwgdGFibGUsICIucGFycXVldCIpDQogIA0KICAjIFJlYWQgcGFycXVldCBmaWxlIGFuZCBhc3NpZ24gdG8gdmFyaWFibGUgd2l0aCB0YWJsZSBuYW1lDQogIGlmIChmaWxlLmV4aXN0cyhsb2NhbF9maWxlKSkgew0KICAgIGFzc2lnbih0YWJsZSwgcmVhZF9wYXJxdWV0KGxvY2FsX2ZpbGUpKQ0KICAgIG1lc3NhZ2UoIkxvYWRlZCAiLCB0YWJsZSwgIiAoIiwgbnJvdyhnZXQodGFibGUpKSwgIiByb3dzKSIpDQogIH0gZWxzZSB7DQogICAgd2FybmluZygiRmlsZSBub3QgZm91bmQ6ICIsIGxvY2FsX2ZpbGUpDQogIH0NCn0NCg0KDQpzb3VyY2UoInByZXBkYXRhaGVscGVyLnIiKQ0KDQoNCmhvbGRlck9SaW52ZW50b3I9aG9sZGVyX2NvdW50cmllcyAlPiUgbXV0YXRlKHR5cGU9ImhvbGRlciIpICU+JQ0KICBiaW5kX3Jvd3MoaW52ZW50b3JfY291bnRyaWVzICU+JSBtdXRhdGUodHlwZT0iaW52ZW50b3IiKSkgJT4lIHJlbmFtZShpc28yPXBlcnNvbl9jdHJ5X2NvZGUpICU+JSANCiAgaW5uZXJfam9pbihjb3VudHJ5X2luY29tZSkNCg0KDQpsaWJyYXJ5KGNvbGxhcHNlKQ0KDQpoaWNpbm5vIDwtIGhvbGRlck9SaW52ZW50b3IgJT4lDQogIGZncm91cF9ieShkb2NkYl9mYW1pbHlfaWQpICU+JQ0KICBmc3VtbWFyaXNlKA0KICAgIGhpY2lubm8gPSBmbWF4KEhJQyksDQogICAgaGljaW5ub1VTID0gZm1heChpc28yID09ICJVUyIpLA0KICAgIGhpY2lubm9FdXJvcGUgPSBmbWF4KGlzbzIgJWluJSBoaWNldXJvcGUpLA0KICAgIG11bHRpID0gZm5kaXN0aW5jdChpc28yKSA+IDEsDQogICAgaGljaW52ZW50b3IgPSBmbWF4KEhJQyAqICh0eXBlID09ICJpbnZlbnRvciIpKSwNCiAgICBoaWNob2xkZXIgPSBmbWF4KEhJQyAqICh0eXBlID09ICJob2xkZXIiKSkNCiAgKQ0KDQoNCg0KDQpjb3VudHJ5bWFwIDwtIHJlYWRfcGFycXVldCgiLi4vaXNlYXBwL2NvdW50cnltYXAucGFycXVldCIpDQojZm9yIChmZiBpbiBmaWxlcykgew0KIyAgcGF0Y2hhcl9jb3VudHJ5bWFwIDwtIHBhdGNoYXJfY291bnRyeW1hcCAlPiUgbGVmdF9qb2luKHJlYWRfcGFycXVldChmZikpDQojfQ0KDQp0ZWNobWFwIDwtIHJlYWRfcGFycXVldCgiLi4vaXNlYXBwL3RlY2htYXAucGFycXVldCIpDQp0ZXN0PXRlY2htYXAgJT4lIGRpc3RpbmN0KHRlY2hub2xvZ3kpDQpncmVlbmlkID0gdGVjaG1hcCAlPiUgZmlsdGVyKHRlY2hub2xvZ3k9PSJBbnkgR3JlZW4iKSAlPiUgZGlzdGluY3QoKQ0KDQoNCmlzdHJheGdsb2JhbD0gcmVhZF9wYXJxdWV0KCIuLi9pc2VhcHAvaXN0cmF4ZXMvaXN0cmF4X2dsb2JhbC5wYXJxdWV0IikNCmlzdHJheGVtZGVleGNuPSByZWFkX3BhcnF1ZXQoIi4uL2lzZWFwcC9pc3RyYXhlcy9pc3RyYXhfZW1kZWV4Y24ucGFycXVldCIpDQppc3RyYXhuYXRpb25hbD0gcmVhZF9wYXJxdWV0KCIuLi9pc2VhcHAvaXN0cmF4ZXMvaXN0cmF4X25hdGlvbmFsa2V5XzIwMDlfMjAxOC5wYXJxdWV0IikNCg0KI2JpZ1g9Y291bnRyeW1hcCAlPiUgaW5uZXJfam9pbihoaWNpbm5vKSAlPiUgaW5uZXJfam9pbihpc3RyYXhnbG9iYWwpDQoNCg0KYmlnWD1jb3VudHJ5bWFwICU+JSBpbm5lcl9qb2luKGhpY2lubm8pICU+JSANCiAgIGlubmVyX2pvaW4oaXN0cmF4Z2xvYmFsKSAlPiUNCiAgIGlubmVyX2pvaW4oaXN0cmF4ZW1kZWV4Y24pICU+JSANCiAgIGlubmVyX2pvaW4oaXN0cmF4bmF0aW9uYWwpICU+JSANCiAgIHJlbmFtZShpc28yPWN0cnlfY29kZSkgJT4lIA0KICAgaW5uZXJfam9pbihjb3VudHJ5X2luY29tZSklPiUgbXV0YXRlKENOaWQ9aXNvMj09IkNOIikgJT4lIGxlZnRfam9pbihncmVlbmlkKSAlPiUgDQogICBtdXRhdGUoYW55Z3JlZW49IWlzLm5hKHRlY2hub2xvZ3kpKQ0KDQoNCmJpZ1g9Y291bnRyeW1hcCAlPiUgbGVmdF9qb2luKGhpY2lubm8pICU+JSANCiAgIGlubmVyX2pvaW4oaXN0cmF4Z2xvYmFsKSAlPiUNCiAgIGlubmVyX2pvaW4oaXN0cmF4ZW1kZWV4Y24pICU+JSANCiAgIGlubmVyX2pvaW4oaXN0cmF4bmF0aW9uYWwpICU+JSANCiAgIHJlbmFtZShpc28yPWN0cnlfY29kZSkgJT4lIA0KICAgaW5uZXJfam9pbihjb3VudHJ5X2luY29tZSklPiUgbXV0YXRlKENOaWQ9aXNvMj09IkNOIikgJT4lIGxlZnRfam9pbihncmVlbmlkKSAlPiUgDQogICBtdXRhdGUoYW55Z3JlZW49IWlzLm5hKHRlY2hub2xvZ3kpKQ0KDQoNCg0KbGlicmFyeShmaXhlc3QpDQoNCg0KDQpmZW9scyhpc3RyYXhfZ2xvYmFsIH4gaGljaW5ubytoaWNpbm5vVVMraGljaW5ub0V1cm9wZSttdWx0aSwNCiAgICAgIGJpZ1ggJT4lIGZpbHRlcihISUM9PTAmQ05pZD09MCkpDQoNCmZlb2xzKGlzdHJheF9FTURFZXhDTiB+IGhpY2lubm8raGljaW5ub1VTK2hpY2lubm9FdXJvcGUrbXVsdGksDQogICAgICBiaWdYICU+JSBmaWx0ZXIoSElDPT0wJkNOaWQ9PTApKQ0KDQpmZW9scyhpc3RyYXhfbmF0aW9uYWxrZXlfMjAwOV8yMDE4IH4gaGljaW5ubytoaWNpbm5vVVMraGljaW5ub0V1cm9wZSttdWx0aSwNCiAgICAgIGJpZ1ggJT4lIGZpbHRlcihISUM9PTAmQ05pZD09MCkpDQoNCiMjIyMjIyMjIyMjIyBub3cgZm9jdXMgb24gZ3JlZW4gb25seQ0KDQpmZW9scyhpc3RyYXhfZ2xvYmFsIH4gaGljaW5ubytoaWNpbm5vVVMraGljaW5ub0V1cm9wZSttdWx0aSwNCiAgICAgIGJpZ1ggJT4lIGZpbHRlcihISUM9PTAmQ05pZD09MCZhbnlncmVlbj09VCkpDQoNCmZlb2xzKGlzdHJheF9FTURFZXhDTiB+IGhpY2lubm8raGljaW5ub1VTK2hpY2lubm9FdXJvcGUrbXVsdGksDQogICAgICBiaWdYICU+JSBmaWx0ZXIoSElDPT0wJkNOaWQ9PTAmYW55Z3JlZW49PVQpKQ0KDQpmZW9scyhpc3RyYXhfbmF0aW9uYWxrZXlfMjAwOV8yMDE4IH4gaGljaW5ubytoaWNpbm5vVVMraGljaW5ub0V1cm9wZSttdWx0aSwNCiAgICAgIGJpZ1ggJT4lIGZpbHRlcihISUM9PTAmQ05pZD09MCZhbnlncmVlbj09VCkpDQoNCiMjIyMjIyMjIyMjIyMjIG5vdyB3aXRoIGludGVyYWN0aW9uDQoNCmZlb2xzKGlzdHJheF9nbG9iYWwgfiBoaWNpbm5vKmFueWdyZWVuKw0KICAgICAgICAgICAgICAgICAgICAgIGhpY2lubm9VUyphbnlncmVlbisNCiAgICAgICAgICAgICAgICAgICAgICBoaWNpbm5vRXVyb3BlKmFueWdyZWVuKw0KICAgICAgICAgICAgICAgICAgICAgIG11bHRpKmFueWdyZWVuLA0KICAgICAgYmlnWCAlPiUgZmlsdGVyKEhJQz09MCZDTmlkPT0wKSkNCg0KDQoNCmZlb2xzKGlzdHJheF9FTURFZXhDTiB+IGhpY2lubm8qYW55Z3JlZW4rDQogICAgICAgICAgICAgICAgICAgICAgaGljaW5ub1VTKmFueWdyZWVuKw0KICAgICAgICAgICAgICAgICAgICAgIGhpY2lubm9FdXJvcGUqYW55Z3JlZW4rDQogICAgICAgICAgICAgICAgICAgICAgbXVsdGkqYW55Z3JlZW4sDQogICAgICBiaWdYICU+JSBmaWx0ZXIoSElDPT0wJkNOaWQ9PTApKQ0KDQojIyMjIyMjIyMjIHNpbXBsZXI7IGkuZS4gZHJvcCBldXJvcGUgdXMNCg0KZmVvbHMoaXN0cmF4X2dsb2JhbCB+IGhpY2lubm8qYW55Z3JlZW4rbXVsdGkqYW55Z3JlZW4sDQogICAgICAgICAgICAgICAgICAgICAgDQogICAgICBiaWdYICU+JSBmaWx0ZXIoSElDPT0wJkNOaWQ9PTApKQ0KDQoNCg0KZmVvbHMoaXN0cmF4X0VNREVleENOIH4gaGljaW5ubyphbnlncmVlbittdWx0aSphbnlncmVlbiwNCiAgICAgICAgICAgICAgICAgICAgICANCiAgICAgIGJpZ1ggJT4lIGZpbHRlcihISUM9PTAmQ05pZD09MCkpDQpgYGANCg0KDQojIyMgcGllIGNoYXJ0cw0KYGBge3J9DQoNCg0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojIENyZWF0ZSBzdW1tYXJ5DQpjb2xsYWJfc3VtbWFyeSA8LSBiaWdYICU+JSAgZmlsdGVyKEhJQz09MCZDTmlkPT0wICYgIShoaWNpbm5vID09IDEgJiBtdWx0aSA9PSAwKSkgJT4lIA0KICBjb3VudChoaWNpbm5vLCBtdWx0aSkgJT4lDQogIG11dGF0ZShjYXRlZ29yeSA9IGNhc2Vfd2hlbigNCiAgICBoaWNpbm5vID09IDEgJiBtdWx0aSA9PSAxIH4gIkhJQyAmIEludGVybmF0aW9uYWwiLA0KICAgIGhpY2lubm8gPT0gMSAmIG11bHRpID09IDAgfiAiSElDIE9ubHkiLA0KICAgIGhpY2lubm8gPT0gMCAmIG11bHRpID09IDEgfiAiSW50ZXJuYXRpb25hbCBPbmx5IiwNCiAgICBoaWNpbm5vID09IDAgJiBtdWx0aSA9PSAwIH4gIk5vIENvbGxhYm9yYXRpb24iDQogICkpDQoNCiMgQ3JlYXRlIGludGVyYWN0aXZlIHBpZSBjaGFydA0KcGxvdF9seShjb2xsYWJfc3VtbWFyeSwgDQogICAgICAgIGxhYmVscyA9IH5jYXRlZ29yeSwgDQogICAgICAgIHZhbHVlcyA9IH5uLCANCiAgICAgICAgdHlwZSA9ICdwaWUnLA0KICAgICAgICB0ZXh0aW5mbyA9ICdsYWJlbCtwZXJjZW50K3ZhbHVlJywNCiAgICAgICAgaG92ZXJpbmZvID0gJ3RleHQnLA0KICAgICAgICB0ZXh0ID0gfnBhc3RlKCdDb3VudDonLCBuKSkgJT4lDQogIGxheW91dCh0aXRsZSA9ICdEaXN0cmlidXRpb24gb2YgQ29sbGFib3JhdGlvbiBUeXBlcycpDQoNCmBgYA0KDQojIyMgZG9udXQNCg0KYGBge3J9DQoNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQoNCiMgQ3JlYXRlIHN1bW1hcnkNCmNvbGxhYl9jb3VudHMgPC0gYmlnWCAlPiUNCiAgZmlsdGVyKEhJQz09MCZDTmlkPT0wICYgIShoaWNpbm5vID09IDEgJiBtdWx0aSA9PSAwKSkgJT4lIA0KICBjb3VudChoaWNpbm5vLCBtdWx0aSkgJT4lDQogIA0KICBtdXRhdGUoY2F0ZWdvcnkgPSBjYXNlX3doZW4oDQogICAgaGljaW5ubyA9PSAxICYgbXVsdGkgPT0gMSB+ICJISUMgJiBJbnRlcm5hdGlvbmFsIiwNCiAgICBoaWNpbm5vID09IDEgJiBtdWx0aSA9PSAwIH4gIkhJQyBPbmx5IiwNCiAgICBoaWNpbm5vID09IDAgJiBtdWx0aSA9PSAxIH4gIkludGVybmF0aW9uYWwgT25seSIsDQogICAgaGljaW5ubyA9PSAwICYgbXVsdGkgPT0gMCB+ICJObyBDb2xsYWJvcmF0aW9uIg0KICApLA0KICBwZXJjZW50YWdlID0gcm91bmQobiAvIHN1bShuKSAqIDEwMCwgMSkpDQoNCiMgQ3JlYXRlIGRvbnV0IGNoYXJ0DQpnZ3Bsb3QoY29sbGFiX2NvdW50cywgYWVzKHggPSAyLCB5ID0gbiwgZmlsbCA9IGNhdGVnb3J5KSkgKw0KICBnZW9tX2NvbChjb2xvciA9ICJ3aGl0ZSIsIGxpbmV3aWR0aCA9IDEpICsNCiAgY29vcmRfcG9sYXIodGhldGEgPSAieSIpICsNCiAgeGxpbShjKDAuNSwgMi41KSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKG4sICJcbigiLCBwZXJjZW50YWdlLCAiJSkiKSksDQogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwNCiAgICAgICAgICAgIHNpemUgPSAzLjUsIGZvbnRmYWNlID0gImJvbGQiKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIsIG5hbWUgPSAiQ29sbGFib3JhdGlvbiBUeXBlIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDb2xsYWJvcmF0aW9uIFR5cGVzIikgKw0KICB0aGVtZV92b2lkKCkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpKQ0KDQoNCmBgYA0KDQojIyMgc2ltcGxlIHJlZ3Jlc3Npb25zDQoNCmBgYHtyfQ0KIyMjIyMjIyMjIyMjIyMgbm8gZ3JlZW4NCg0KDQoNCm1vZGVsPWZlb2xzKGlzdHJheF9nbG9iYWwqMTAwIH4gaGljaW5ubyttdWx0aSwNCiAgICAgICAgDQogICAgICBiaWdYICU+JSBmaWx0ZXIoSElDPT0wJkNOaWQ9PTApKQ0KcHJpbnQobW9kZWwpDQoNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoYnJvb20pDQoNCiMgRXh0cmFjdCBhbmQgZmlsdGVyIGZvciBzcGVjaWZpYyB2YXJpYWJsZXMNCmNvZWZfZGF0YSA8LSB0aWR5KG1vZGVsLCBjb25mLmludCA9IFRSVUUpICU+JQ0KICBmaWx0ZXIodGVybSAlaW4lIGMoImhpY2lubm8iLCAibXVsdGlUUlVFIikpICU+JQ0KICBtdXRhdGUodGVybSA9IGZhY3Rvcih0ZXJtLCBsZXZlbHMgPSBjKCJoaWNpbm5vIiwgIm11bHRpVFJVRSIpLA0KICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJISUMgQ29sbGFib3JhdGlvbiIsICJJbnRlcm5hdGlvbmFsIENvbGxhYm9yYXRpb24iKSkpDQoNCiMgQ3JlYXRlIHBsb3QNCmdncGxvdChjb2VmX2RhdGEsIGFlcyh4ID0gdGVybSwgeSA9IGVzdGltYXRlKSkgKw0KICBnZW9tX2NvbChhZXMoZmlsbCA9IHRlcm0pLCBhbHBoYSA9IDAuOCkgKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gY29uZi5sb3csIHltYXggPSBjb25mLmhpZ2gpLCANCiAgICAgICAgICAgICAgICB3aWR0aCA9IDAuMjUsIGxpbmV3aWR0aCA9IDEpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheTMwIikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMkU4NkFCIiwgIiNBMjNCNzIiKSkgKw0KICBsYWJzKHRpdGxlID0gIkVmZmVjdCBvZiBDb2xsYWJvcmF0aW9uIFR5cGUgb24gT3V0Y29tZSIsDQogICAgICAgeCA9IE5VTEwsDQogICAgICAgeSA9ICJFeHRlcm5hbCBWYWx1ZSBwb3N0IHBlciAkMTAwIGludmVzdGVkIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsDQogICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSwNCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpDQoNCg0KDQojIyMjIyBvbmxseSBlbWVkZSBleCBjbg0KDQptb2RlbD1mZW9scyhpc3RyYXhfRU1ERWV4Q04qMTAwIH4gaGljaW5ubyttdWx0aSwNCiAgICAgIGJpZ1ggJT4lIGZpbHRlcihISUM9PTAmQ05pZD09MCkpDQoNCnByaW50KG1vZGVsKQ0KIyBFeHRyYWN0IGFuZCBmaWx0ZXIgZm9yIHNwZWNpZmljIHZhcmlhYmxlcw0KY29lZl9kYXRhIDwtIHRpZHkobW9kZWwsIGNvbmYuaW50ID0gVFJVRSkgJT4lDQogIGZpbHRlcih0ZXJtICVpbiUgYygiaGljaW5ubyIsICJtdWx0aVRSVUUiKSkgJT4lDQogIG11dGF0ZSh0ZXJtID0gZmFjdG9yKHRlcm0sIGxldmVscyA9IGMoImhpY2lubm8iLCAibXVsdGlUUlVFIiksDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkhJQyBDb2xsYWJvcmF0aW9uIiwgIkludGVybmF0aW9uYWwgQ29sbGFib3JhdGlvbiIpKSkNCg0KIyBDcmVhdGUgcGxvdA0KZ2dwbG90KGNvZWZfZGF0YSwgYWVzKHggPSB0ZXJtLCB5ID0gZXN0aW1hdGUpKSArDQogIGdlb21fY29sKGFlcyhmaWxsID0gdGVybSksIGFscGhhID0gMC44KSArDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBjb25mLmxvdywgeW1heCA9IGNvbmYuaGlnaCksIA0KICAgICAgICAgICAgICAgIHdpZHRoID0gMC4yNSwgbGluZXdpZHRoID0gMSkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmF5MzAiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMyRTg2QUIiLCAiI0EyM0I3MiIpKSArDQogIGxhYnModGl0bGUgPSAiRWZmZWN0IG9mIENvbGxhYm9yYXRpb24gVHlwZSBvbiBPdXRjb21lIiwNCiAgICAgICB4ID0gTlVMTCwNCiAgICAgICB5ID0gIkV4dGVybmFsIFZhbHVlIHBvc3QgcGVyICQxMDAgaW52ZXN0ZWQiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMpLA0KICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSkNCg0KDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQpiaWdYYWE9YmlnWCAlPiUgZmlsdGVyKEhJQz09MCAmIENOaWQ9PTApICU+JSBncm91cF9ieShoaWNpbm5vLG11bHRpKSAlPiUgDQogIHN1bW1hcml6ZShpc3RyYXhtZWFuPW1lYW4oaXN0cmF4X2dsb2JhbCksDQogICAgICAgICAgICBpc3RyYXhwOTA9cXVhbnRpbGUoaXN0cmF4X2dsb2JhbCwgMC45MCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIG4oKSkgDQoNCg0KDQpiaWdYYT1iaWdYICU+JSBncm91cF9ieShISUMsaGljaW5ubyxtdWx0aSxDTmlkKSAlPiUgDQogIHN1bW1hcml6ZShpc3RyYXhtZWFuPW1lYW4oaXN0cmF4X2dsb2JhbCksDQogICAgICAgICAgICBpc3RyYXhwOTA9cXVhbnRpbGUoaXN0cmF4X2dsb2JhbCwgMC45MCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIG4oKSkgJT4lIGZpbHRlcighKEhJQz09MSZoaWNpbm5vPT0wKSkNCg0KDQoNCg0KDQoNCmBgYA0KDQoNCg0KYGBge3J9DQoNCnBsb3RfY29sbGFiX3JldHVybnMgPC0gZnVuY3Rpb24oZGF0YSA9IGJpZ1gsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXJfSElDID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyX0NOaWQgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNsdWRlX0hJQ19vbmx5ID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiUmV0dXJuIGJ5IENvbGxhYm9yYXRpb24gVHlwZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsYWIgPSAiU3BpbGxvdmVyIHJldHVybiBwZXIgJDEwMCBpbnZlc3RlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11bHRpcGx5X2J5ID0gMTAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWxldHRlID0gIlNldDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0X3NpemUgPSAzLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsX3NpemUgPSA0KSB7DQogIA0KICB2YXJfZW5xdW8gPC0gZW5xdW8odmFyKQ0KICANCiAgIyBBcHBseSBmaWx0ZXJzIGNvbmRpdGlvbmFsbHkNCiAgZmlsdGVyZWRfZGF0YSA8LSBkYXRhDQogIGlmIChmaWx0ZXJfSElDKSBmaWx0ZXJlZF9kYXRhIDwtIGZpbHRlcmVkX2RhdGEgJT4lIGZpbHRlcihISUMgPT0gMCkNCiAgaWYgKGZpbHRlcl9DTmlkKSBmaWx0ZXJlZF9kYXRhIDwtIGZpbHRlcmVkX2RhdGEgJT4lIGZpbHRlcihDTmlkID09IDApDQogIGlmIChleGNsdWRlX0hJQ19vbmx5KSBmaWx0ZXJlZF9kYXRhIDwtIGZpbHRlcmVkX2RhdGEgJT4lIGZpbHRlcighKGhpY2lubm8gPT0gMSAmIG11bHRpID09IDApKQ0KICANCiAgIyBQcmVwYXJlIHN1bW1hcnkgZGF0YQ0KICBzdW1tYXJ5X2RhdGEgPC0gZmlsdGVyZWRfZGF0YSAlPiUNCiAgICBtdXRhdGUoY29sbGFiX3R5cGUgPSBjYXNlX3doZW4oDQogICAgICBoaWNpbm5vID09IDEgJiBtdWx0aSA9PSAxIH4gIkhJQyAmIEludGVybmF0aW9uYWwiLA0KICAgICAgaGljaW5ubyA9PSAxICYgbXVsdGkgPT0gMCB+ICJISUMgT25seSIsDQogICAgICBoaWNpbm5vID09IDAgJiBtdWx0aSA9PSAxIH4gIkludGVybmF0aW9uYWwgT25seSIsDQogICAgICBoaWNpbm5vID09IDAgJiBtdWx0aSA9PSAwIH4gIk5vIENvbGxhYm9yYXRpb24iDQogICAgKSkgJT4lDQogICAgZ3JvdXBfYnkoY29sbGFiX3R5cGUpICU+JQ0KICAgIHN1bW1hcmlzZSgNCiAgICAgIGF2Z192YWx1ZSA9IG1lYW4oISF2YXJfZW5xdW8gKiBtdWx0aXBseV9ieSwgbmEucm0gPSBUUlVFKSwNCiAgICAgIG4gPSBuKCksDQogICAgICBubiA9IGxvZyhuKSwNCiAgICAgIC5ncm91cHMgPSAiZHJvcCINCiAgICApICU+JQ0KICAgIG11dGF0ZSgNCiAgICAgIHNoYXJlID0gbiAvIHN1bShuKSwNCiAgICAgIGxvZ19zaGFyZSA9IG5uIC8gc3VtKG5uKSwNCiAgICAgIGxvZ19zaGFyZV9zY2FsZWQgPSAobG9nX3NoYXJlIC0gbWluKGxvZ19zaGFyZSkpICogNTAsDQogICAgICB5X3BvcyA9IHJvd19udW1iZXIoKSwNCiAgICAgIHltaW4gPSB5X3BvcyAtIGxvZ19zaGFyZS8yLA0KICAgICAgeW1heCA9IHlfcG9zICsgbG9nX3NoYXJlLzINCiAgICApICU+JQ0KICAgIGFycmFuZ2UoZGVzYyhhdmdfdmFsdWUpKQ0KICANCiAgIyBDcmVhdGUgcGxvdA0KICBnZ3Bsb3Qoc3VtbWFyeV9kYXRhKSArDQogICAgZ2VvbV9yZWN0KGFlcyh4bWluID0gMCwgeG1heCA9IGF2Z192YWx1ZSwgeW1pbiA9IHltaW4sIHltYXggPSB5bWF4LCBmaWxsID0gY29sbGFiX3R5cGUpLA0KICAgICAgICAgICAgICBhbHBoYSA9IDAuOCwgY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAxKSArDQogICAgZ2VvbV90ZXh0KGFlcyh4ID0gYXZnX3ZhbHVlICsgbWF4KGF2Z192YWx1ZSkqMC4wMiwgeSA9IHlfcG9zLCANCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUwKHJvdW5kKGF2Z192YWx1ZSwgMikpKSwNCiAgICAgICAgICAgICAgaGp1c3QgPSAwLCBzaXplID0gbGFiZWxfc2l6ZSwgZm9udGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gImJsYWNrIikgKw0KICAgIGdlb21fdGV4dChhZXMoeCA9IDAsIHkgPSB5X3BvcywgDQogICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlMChjb2xsYWJfdHlwZSwgIlxuKG49Iiwgc2NhbGVzOjpjb21tYShuKSwgIiwgIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKHNoYXJlKjEwMCwgMSksICIlKSIpKSwNCiAgICAgICAgICAgICAgaGp1c3QgPSAxLjA1LCBzaXplID0gdGV4dF9zaXplLCBmb250ZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiYmxhY2siKSArDQogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9IHBhbGV0dGUpICsNCiAgICBsYWJzKHRpdGxlID0gdGl0bGUsDQogICAgICAgICBzdWJ0aXRsZSA9ICJCYXIgd2lkdGggcHJvcG9ydGlvbmFsIHRvIGxvZyhzaGFyZSkgb2Ygb2JzZXJ2YXRpb25zIiwNCiAgICAgICAgIHggPSB4bGFiLA0KICAgICAgICAgeSA9IE5VTEwpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgIHRoZW1lKA0KICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQpLA0KICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxMCwgMTAsIDEwLCAxMDApDQogICAgKSArDQogICAgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIikNCn0NCg0KIyBVc2FnZToNCnBsb3RfY29sbGFiX3JldHVybnMoYmlnWCwgaXN0cmF4X2dsb2JhbCkNCnBsb3RfY29sbGFiX3JldHVybnMoYmlnWCwgaXN0cmF4X0VNREVleENOKQ0KDQoNCg0KYGBgDQoNCiMjIGZpbmQgbG9jYXRpb25zLi4uDQoNCndlIGFyZSB1c2luZyB0aWR5Z2VvY29kZXIgJiBtYXBib3guLi4NCg0KYGBge3J9DQoNCmxpYnJhcnkoYXJyb3cpDQpkZj1yZWFkX3BhcnF1ZXQocGFzdGUwKGxvY2FsYmlnLCJcXGRhdGFcXHBlcnNvbl9hZGRyZXNzLnBhcnF1ZXQiKSkNCg0KbGlicmFyeSh0aWR5Z2VvY29kZXIpDQoNCiMgY3JlYXRlIGEgZGF0YWZyYW1lIHdpdGggYWRkcmVzc2VzDQoNCiMjIGxldCdzIGNoZWNrIGlmIHdlIGhhdmUgc3RvcmVkIHN1Y2ggYSBkYXRhZnJhbWUgcHJldmlvdXNseQ0KbWJveGZpbGU9cGFzdGUwKGxvY2FsYmlnLCJcXGRhdGFcXGxhdF9sb25nc19tYXBib3gucGFycXVldCIpDQppZiAoIWZpbGUuZXhpc3RzKG1ib3hmaWxlKSkgew0KICAjZGY9ZGYgJT4lIG11dGF0ZShycj1ydW5pZigxOm4oKSkpICAlPiUgYXJyYW5nZShycikgJT4lIHNlbGVjdCgtcnIpDQogIHNvbWVfYWRkcmVzc2VzPWRmICU+JSBoZWFkKDEwKSAlPiUgbXV0YXRlKGFkZHJlc3NzdHI9cGFzdGUocGVyc29uX2FkZHJlc3MscGVyc29uX2N0cnlfY29kZSxzZXA9IiwiKSkNCiAgIyBnZW9jb2RlIHRoZSBhZGRyZXNzZXMNCiAgbGF0X2xvbmdzX21hcGJveCA8LSBzb21lX2FkZHJlc3NlcyAlPiUNCiAgICBnZW9jb2RlKGFkZHJlc3NzdHIsIG1ldGhvZCA9ICdtYXBib3gnLCANCiAgICAgICAgICAgICNmbGF0dGVuPVQsDQogICAgICAgICAgICAjZnVsbF9yZXN1bHRzPVQsDQogICAgICAgICAgICAjaWQgPSBpZCwNCiAgICAgICAgICAgIA0KICAgICAgICAgICAgI3R5cGU9IHR5cGUsDQogICAgICAgICAgICBsYXQgPSBsYXRpdHVkZSAsIGxvbmcgPSBsb25naXR1ZGUpDQogICM+IFBhc3NpbmcgMyBhZGRyZXNzZXMgdG8gdGhlIE5vbWluYXRpbSBzaW5nbGUgYWRkcmVzcyBnZW9jb2Rlcg0KICAjPiBRdWVyeSBjb21wbGV0ZWQgaW46IDMuMiBzZWNvbmRzDQp9IGVsc2V7DQogIA0KICBsYXRfbG9uZ3NfbWFwYm94PXJlYWRfcGFycXVldChtYm94ZmlsZSkNCiAgDQp9DQoNCg0KDQpyZWN1cnNpdmVfbWFwYm94PWZ1bmN0aW9uKCl7DQogIGRmX2ZpbHRlcmVkIDwtIGRmICU+JQ0KICAgIGFudGlfam9pbihsYXRfbG9uZ3NfbWFwYm94LCBieSA9ICJwZXJzb25faWQiKQ0KICANCiAgZGZfZmlsdGVyZWQ9ZGZfZmlsdGVyZWQgJT4lIG11dGF0ZShycj1ydW5pZigxOm4oKSkpICAlPiUgYXJyYW5nZShycikgJT4lIHNlbGVjdCgtcnIpDQogIA0KICBzb21lX2FkZHJlc3Nlcz1kZl9maWx0ZXJlZCAlPiUgaGVhZCgxNTAwKSAlPiUgIG11dGF0ZShhZGRyZXNzc3RyPXBhc3RlKHBlcnNvbl9hZGRyZXNzLHBlcnNvbl9jdHJ5X2NvZGUsc2VwPSIsIikpDQogIHByaW50KG5yb3coZGZfZmlsdGVyZWQpKQ0KICBsYXRfbG9uZ3NfbWFwYm94MiA8LSBzb21lX2FkZHJlc3NlcyAlPiUNCiAgICBnZW9jb2RlKGFkZHJlc3NzdHIsIG1ldGhvZCA9ICdtYXBib3gnLCBsYXQgPSBsYXRpdHVkZSAsIGxvbmcgPSBsb25naXR1ZGUsZnVsbF9yZXN1bHRzPUYpDQogIA0KICANCiAgbGF0X2xvbmdzX21hcGJveF9uZXc9bGF0X2xvbmdzX21hcGJveCAlPiUgYmluZF9yb3dzKGxhdF9sb25nc19tYXBib3gyKQ0KICAjbGF0X2xvbmdzX21hcGJveD1sYXRfbG9uZ3NfbWFwYm94ICU+JSBiaW5kX3Jvd3MobGF0X2xvbmdzX21hcGJveDIpDQogIHJldHVybihsYXRfbG9uZ3NfbWFwYm94X25ldykNCn0NCg0KI2xhdF9sb25nc19tYXBib3g9bGF0X2xvbmdzX21hcGJveF9uZXcgICU+JSBkaXN0aW5jdCgpDQojbGF0X2xvbmdzX21hcGJveD1sYXRfbG9uZ3NfbWFwYm94ICAlPiUgZGlzdGluY3QoKSAlPiUgc2VsZWN0KC1ycikNCg0KDQoNCmxhdF9sb25nc19tYXBib3g9cmVjdXJzaXZlX21hcGJveCgpDQp3cml0ZV9wYXJxdWV0KGxhdF9sb25nc19tYXBib3gscGFzdGUwKGxvY2FsYmlnLCJcXGRhdGFcXGxhdF9sb25nc19tYXBib3gucGFycXVldCIpKQ0KDQoNCg0KDQoNCmBgYA0KDQojIyMgcmVhZGluZyBiYWNrIGluIGZyb20gZGlzaw0KDQpgYGB7cn0NCmxhdF9sb25nc19tYXBib3g9cmVhZF9wYXJxdWV0KHBhc3RlMChsb2NhbGJpZywiXFxkYXRhXFxsYXRfbG9uZ3NfbWFwYm94LnBhcnF1ZXQiKSkNCg0KDQpgYGANCg0KDQoNCg0KIyMjIyBDbGVhbmluZyB1cA0KV2UgZXhhbWluZSBpZiB0aGUgcG9pbnRzIHN1Z2dlc3RlZCBmYWxsIHdpdGhpbiB0aGUgY291bnRyeSBib3VuZGFyaWVzLCB3aGljaCBkb2Vzbid0IA0KYWx3YXlzIHNlZW0gdGhlIGNhc2UuLi4NCg0KDQpgYGB7cn0NCg0KdGVzdD1sYXRfbG9uZ3NfbWFwYm94ICU+JSBmaWx0ZXIobmNoYXIocGVyc29uX2FkZHJlc3MpPD0yKQ0KDQp0ZXN0ZGY9bGF0X2xvbmdzX21hcGJveCAlPiUgZmlsdGVyKCFpcy5uYShwZXJzb25fY3RyeV9jb2RlKSAmICFpcy5uYShsb25naXR1ZGUpKQ0KDQoNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHJuYXR1cmFsZWFydGgpDQoNCiMgR2V0IHdvcmxkIGJvdW5kYXJpZXMNCndvcmxkIDwtIG5lX2NvdW50cmllcyhzY2FsZSA9ICJtZWRpdW0iLCByZXR1cm5jbGFzcyA9ICJzZiIpDQoNCg0KDQoNCg0KDQoNCg0Kd29ybGQgPC0gd29ybGQgJT4lDQogIG11dGF0ZShpc29fYTJfZml4ZWQgPSBjYXNlX3doZW4oDQogICAgbmFtZSA9PSAiVGFpd2FuIiB+ICJUVyIsDQogICAgbmFtZSA9PSAiS29zb3ZvIiB+ICJYSyIsDQogICAgaXNvX2EyID09ICItOTkiICYgIWlzLm5hKGlzb19hMykgfiBpc29fYTMsICAjIEZhbGxiYWNrIHRvIGlzb19hMw0KICAgIFRSVUUgfiBpc29fYTINCiAgKSkNCg0KDQoNCg0KDQoNCg0KDQoNCg0KIyBDb252ZXJ0IGRhdGFmcmFtZSB0byBzcGF0aWFsIHBvaW50cw0KZGZfc2YgPC0gc3RfYXNfc2YodGVzdGRmLCANCiAgICAgICAgICAgICAgICAgIGNvb3JkcyA9IGMoImxvbmdpdHVkZSIsICJsYXRpdHVkZSIpLCANCiAgICAgICAgICAgICAgICAgIGNycyA9IDQzMjYpDQoNCg0KYGBgDQoNCg0KIyBwcmVwYXJlIHByb2dyZXNzIGJhcg0KbGlicmFyeShwcm9ncmVzcykNCg0KbiA8LSBucm93KHRlc3RkZikNCg0KcGIgPC0gcHJvZ3Jlc3NfYmFyJG5ldygNCiAgZm9ybWF0ID0gIls6YmFyXSA6cGVyY2VudCBldGE6IDpldGEiLA0KICB0b3RhbCA9IG4sDQogIGNsZWFyID0gRkFMU0UsDQogIHdpZHRoID0gNjANCikNCg0KDQoNCiMgSW5pdGlhbGl6ZSBvayBjb2x1bW4NCnRlc3RkZiRvayA8LSBGQUxTRQ0KDQojIENoZWNrIGVhY2ggcG9pbnQNCmZvcihpIGluIDE6bnJvdyh0ZXN0ZGYpKSB7DQogIGNvdW50cnlfcG9seSA8LSB3b3JsZFt3b3JsZCRpc29fYTIgPT0gdGVzdGRmJHBlcnNvbl9jdHJ5X2NvZGVbaV0sIF0NCiAgaWYobnJvdyhjb3VudHJ5X3BvbHkpID4gMCkgew0KICAgIHRlc3RkZiRva1tpXSA8LSBzdF9pbnRlcnNlY3RzKGRmX3NmW2ksXSwgY291bnRyeV9wb2x5LCBzcGFyc2UgPSBGQUxTRSlbMSwxXQ0KICB9DQogIHBiJHRpY2soKQ0KfQ0KDQoNCg0KYGBge3J9DQoNCg0KDQojIERldGVjdCBudW1iZXIgb2YgY29yZXMNCmxpYnJhcnkocGFyYWxsZWwpDQpuX2NvcmVzIDwtIGRldGVjdENvcmVzKCkgLSAyICAjIExlYXZlIG9uZSBjb3JlIGZyZWUNCg0KIyBDcmVhdGUgY2x1c3Rlcg0KY2wgPC0gbWFrZUNsdXN0ZXIobl9jb3JlcykNCg0KIyBFeHBvcnQgbmVjZXNzYXJ5IG9iamVjdHMgdG8gY2x1c3Rlcg0KY2x1c3RlckV4cG9ydChjbCwgYygiZGZfc2YiLCAid29ybGQiLCAidGVzdGRmIiksIGVudmlyID0gZW52aXJvbm1lbnQoKSkNCmNsdXN0ZXJFdmFsUShjbCwgbGlicmFyeShzZikpDQoNCiMgUGFyYWxsZWwgcHJvY2Vzc2luZw0KdGVzdGRmJG9rIDwtIHBhclNhcHBseShjbCwgMTpucm93KHRlc3RkZiksIGZ1bmN0aW9uKGkpIHsNCiAgY291bnRyeV9wb2x5IDwtIHdvcmxkW3dvcmxkJGlzb19hMiA9PSB0ZXN0ZGYkcGVyc29uX2N0cnlfY29kZVtpXSwgXQ0KICBpZihucm93KGNvdW50cnlfcG9seSkgPiAwKSB7DQogICAgcmV0dXJuKHN0X2ludGVyc2VjdHMoZGZfc2ZbaSxdLCBjb3VudHJ5X3BvbHksIHNwYXJzZSA9IEZBTFNFKVsxLDFdKQ0KICB9IGVsc2Ugew0KICAgIHJldHVybihGQUxTRSkNCiAgfQ0KfSkNCg0KDQojIFN0b3AgY2x1c3Rlcg0Kc3RvcENsdXN0ZXIoY2wpDQoNCg0KdGVzdD10ZXN0ZGYgJT4lIGZpbHRlcihwZXJzb25fY3RyeV9jb2RlPT0iVFciKQ0KDQoNCiMgY29ycmVjdCBzb21lIGlzc3Vlcw0KdGVzdGRmPXRlc3RkZiAlPiUgbXV0YXRlKG9rPWlmZWxzZShwZXJzb25fY3RyeV9jb2RlPT0iVFciICxULG9rKSwgIyBjb3JyZWN0IHNvbWUgQ2hpbmVzIGNvcnJ1cHRpb24gb2YgdGhlIHNoYXBlZmlsZQ0KICAgICAgICAgICAgICAgICAgICAgICAgIG9rPWlmZWxzZShuY2hhcihnc3ViKCJcXHMrIiwgIiIsIHBlcnNvbl9jdHJ5X2NvZGUpKTwyICxULG9rKSApDQoNCg0KDQoNCg0KDQpgYGANCg0KDQoNCiMjIyBJbmZlciBpc28yDQoNCmBgYHtyfQ0KDQoNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHJuYXR1cmFsZWFydGgpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojIEdldCB3b3JsZCBib3VuZGFyaWVzDQp3b3JsZCA8LSBuZV9jb3VudHJpZXMoc2NhbGUgPSAibWVkaXVtIiwgcmV0dXJuY2xhc3MgPSAic2YiKQ0KDQojIEluaXRpYWxpemUgaW5mZXJyZWRpc28yIGNvbHVtbg0KbGF0X2xvbmdzX21hcGJveCA8LSBsYXRfbG9uZ3NfbWFwYm94ICU+JQ0KICBtdXRhdGUoaW5mZXJyZWRpc28yID0gTkFfY2hhcmFjdGVyXykNCg0KIyBGaWx0ZXIgdG8gb25seSByb3dzIHdpdGggdmFsaWQgY29vcmRpbmF0ZXMNCnZhbGlkX2Nvb3JkcyA8LSAhaXMubmEobGF0X2xvbmdzX21hcGJveCRsYXRpdHVkZSkgJiANCiAgICAgICAgICAgICAgICAhaXMubmEobGF0X2xvbmdzX21hcGJveCRsb25naXR1ZGUpDQoNCmlmKHN1bSh2YWxpZF9jb29yZHMpID4gMCkgew0KICAjIENvbnZlcnQgdG8gc3BhdGlhbCBwb2ludHMgKG9ubHkgdmFsaWQgY29vcmRpbmF0ZXMpDQogIHBvaW50c19zZiA8LSBzdF9hc19zZihsYXRfbG9uZ3NfbWFwYm94W3ZhbGlkX2Nvb3JkcywgXSwgDQogICAgICAgICAgICAgICAgICAgICAgICBjb29yZHMgPSBjKCJsb25naXR1ZGUiLCAibGF0aXR1ZGUiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICBjcnMgPSA0MzI2KQ0KICANCiAgIyBTcGF0aWFsIGpvaW4gdG8gZmluZCB3aGljaCBjb3VudHJ5IHBvbHlnb24gY29udGFpbnMgZWFjaCBwb2ludA0KICBwb2ludHNfd2l0aF9jb3VudHJ5IDwtIHN0X2pvaW4ocG9pbnRzX3NmLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdvcmxkICU+JSBzZWxlY3QoaXNvX2EyKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBqb2luID0gc3RfaW50ZXJzZWN0cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZnQgPSBUUlVFKQ0KICANCiAgIyBFeHRyYWN0IGluZmVycmVkaXNvMg0KICBsYXRfbG9uZ3NfbWFwYm94JGluZmVycmVkaXNvMlt2YWxpZF9jb29yZHNdIDwtIHBvaW50c193aXRoX2NvdW50cnkkaXNvX2EyDQogIA0KICAjIFNwZWNpYWwgaGFuZGxpbmcgZm9yIFRhaXdhbg0KICAjIERlZmluZSBUYWl3YW4ncyBib3VuZGluZyBib3ggd2l0aCBzb21lIGJ1ZmZlcg0KICB0YWl3YW5fYm91bmRzIDwtIGxpc3QoDQogICAgbGF0X21pbiA9IDIxLjgsDQogICAgbGF0X21heCA9IDI1LjQsDQogICAgbG9uX21pbiA9IDExOS4zLA0KICAgIGxvbl9tYXggPSAxMjIuMQ0KICApDQogIA0KICAjIElkZW50aWZ5IHBvaW50cyBpbiBUYWl3YW4gcmVnaW9uDQogIGluX3RhaXdhbl9yZWdpb24gPC0gdmFsaWRfY29vcmRzICYgDQogICAgICAgICAgICAgICAgICAgICAgbGF0X2xvbmdzX21hcGJveCRsYXRpdHVkZSA+PSB0YWl3YW5fYm91bmRzJGxhdF9taW4gJiANCiAgICAgICAgICAgICAgICAgICAgICBsYXRfbG9uZ3NfbWFwYm94JGxhdGl0dWRlIDw9IHRhaXdhbl9ib3VuZHMkbGF0X21heCAmIA0KICAgICAgICAgICAgICAgICAgICAgIGxhdF9sb25nc19tYXBib3gkbG9uZ2l0dWRlID49IHRhaXdhbl9ib3VuZHMkbG9uX21pbiAmIA0KICAgICAgICAgICAgICAgICAgICAgIGxhdF9sb25nc19tYXBib3gkbG9uZ2l0dWRlIDw9IHRhaXdhbl9ib3VuZHMkbG9uX21heA0KICANCiAgIyBPdmVycmlkZSB3aXRoIFRXIGZvciBwb2ludHMgaW4gVGFpd2FuIHJlZ2lvbiAocmVnYXJkbGVzcyBvZiB3aGF0IHNwYXRpYWwgam9pbiBzYWlkKQ0KICBsYXRfbG9uZ3NfbWFwYm94JGluZmVycmVkaXNvMltpbl90YWl3YW5fcmVnaW9uXSA8LSAiVFciDQp9DQoNCg0KYGBgDQoNCmNsZWFuIHVwIHNvbWUgbW9yZS4uLg0KYGBge3J9DQoNCmxhdF9sb25nc19tYXBib3gkaW5mZXJyZWRpc28yW2xhdF9sb25nc19tYXBib3gkaW5mZXJyZWRpc28yPT0iLTk5InxsYXRfbG9uZ3NfbWFwYm94JGluZmVycmVkaXNvMj09IiJdIDwtIE5BDQoNCmxhdF9sb25nc19tYXBib3g9IGxhdF9sb25nc19tYXBib3ggJT4lIG11dGF0ZShwZXJzb25fY3RyeV9jb2RlPWdzdWIoIlxccysiLCAiIiwgcGVyc29uX2N0cnlfY29kZSkpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKHBlcnNvbl9jdHJ5X2NvZGU9aWZlbHNlKHBlcnNvbl9jdHJ5X2NvZGU9PSIiICYgIWlzLm5hKGluZmVycmVkaXNvMiksaW5mZXJyZWRpc28yLHBlcnNvbl9jdHJ5X2NvZGUpKSANCg0KDQp0ZXN0PWxhdF9sb25nc19tYXBib3ggJT4lIGZpbHRlcihwZXJzb25fY3RyeV9jb2RlPT0iVFciKQ0KDQoNCg0KbGF0X2xvbmdzX21hcGJveD1sYXRfbG9uZ3NfbWFwYm94ICU+JSBmaWx0ZXIoIWlzLm5hKGxhdGl0dWRlKSApICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCFpcy5uYShwZXJzb25fY3RyeV9jb2RlKSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIocGVyc29uX2N0cnlfY29kZSE9IiIpICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCEocGVyc29uX2N0cnlfY29kZSE9aW5mZXJyZWRpc28yICYgIWlzLm5hKGluZmVycmVkaXNvMikgKSkNCg0KDQoNCmxhdF9sb25nc19ieWNvdW50cnk9bGF0X2xvbmdzX21hcGJveCAlPiUgZ3JvdXBfYnkocGVyc29uX2N0cnlfY29kZSxsb25naXR1ZGUsbGF0aXR1ZGUpICU+JSBzdW1tYXJpc2UoaW5ub3M9bigpKQ0KDQpgYGANCg0KDQoNCkxldCdzIGhhdmUgYSBsb29rOg0KDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KGxlYWZsZXQpDQoNCiMgV2l0aCBjbHVzdGVyaW5nIChtdWNoIGJldHRlciBmb3IgMTAwayBwb2ludHMhKQ0KbGVhZmxldChsYXRfbG9uZ3NfbWFwYm94KSAlPiUNCiAgYWRkVGlsZXMoKSAlPiUNCiAgYWRkQ2lyY2xlTWFya2VycygNCiAgICBsbmcgPSB+bG9uZ2l0dWRlLA0KICAgIGxhdCA9IH5sYXRpdHVkZSwNCiAgICByYWRpdXMgPSAzLA0KICAgIGZpbGxPcGFjaXR5ID0gMC41LA0KICAgIHN0cm9rZSA9IEZBTFNFLA0KICAgIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoKQ0KICApDQoNCg0KDQoNCmBgYA0KDQoNCiMgZGVhbCB3aXRoIGNhcGl0YWwgY2l0aWVzIC4uLi4gZ29ubmEgYmUgdXNlZCBhcyBkZWZhdWx0Li4uDQoNCg0KYGBge3J9DQoNCg0KIyBDcmVhdGUgYSBjb21wcmVoZW5zaXZlIGRhdGFmcmFtZSBvZiBjYXBpdGFsIGNpdGllcyB3aXRoIGNvb3JkaW5hdGVzDQpjYXBpdGFsX2Nvb3JkcyA8LSBkYXRhLmZyYW1lKA0KICBpc28yID0gYygNCiAgICAiQUYiLCAiQUwiLCAiRFoiLCAiQUQiLCAiQU8iLCAiQUciLCAiQVIiLCAiQU0iLCAiQVUiLCAiQVQiLA0KICAgICJBWiIsICJCUyIsICJCSCIsICJCRCIsICJCQiIsICJCWSIsICJCRSIsICJCWiIsICJCSiIsICJCVCIsDQogICAgIkJPIiwgIkJBIiwgIkJXIiwgIkJSIiwgIkJOIiwgIkJHIiwgIkJGIiwgIkJJIiwgIktIIiwgIkNNIiwNCiAgICAiQ0EiLCAiQ1YiLCAiQ0YiLCAiVEQiLCAiQ0wiLCAiQ04iLCAiQ08iLCAiS00iLCAiQ0ciLCAiQ0QiLA0KICAgICJDUiIsICJDSSIsICJIUiIsICJDVSIsICJDWSIsICJDWiIsICJESyIsICJESiIsICJETSIsICJETyIsDQogICAgIkVDIiwgIkVHIiwgIlNWIiwgIkdRIiwgIkVSIiwgIkVFIiwgIkVUIiwgIkZKIiwgIkZJIiwgIkZSIiwNCiAgICAiR0EiLCAiR00iLCAiR0UiLCAiREUiLCAiR0giLCAiR1IiLCAiR0QiLCAiR1QiLCAiR04iLCAiR1ciLA0KICAgICJHWSIsICJIVCIsICJITiIsICJIVSIsICJJUyIsICJJTiIsICJJRCIsICJJUiIsICJJUSIsICJJRSIsDQogICAgIklMIiwgIklUIiwgIkpNIiwgIkpQIiwgIkpPIiwgIktaIiwgIktFIiwgIktJIiwgIktQIiwgIktSIiwNCiAgICAiS1ciLCAiS0ciLCAiTEEiLCAiTFYiLCAiTEIiLCAiTFMiLCAiTFIiLCAiTFkiLCAiTEkiLCAiTFQiLA0KICAgICJMVSIsICJNSyIsICJNRyIsICJNVyIsICJNWSIsICJNViIsICJNTCIsICJNVCIsICJNSCIsICJNUiIsDQogICAgIk1VIiwgIk1YIiwgIkZNIiwgIk1EIiwgIk1DIiwgIk1OIiwgIk1FIiwgIk1BIiwgIk1aIiwgIk1NIiwNCiAgICAiTkEiLCAiTlIiLCAiTlAiLCAiTkwiLCAiTloiLCAiTkkiLCAiTkUiLCAiTkciLCAiTk8iLCAiT00iLA0KICAgICJQSyIsICJQVyIsICJQUyIsICJQQSIsICJQRyIsICJQWSIsICJQRSIsICJQSCIsICJQTCIsICJQVCIsDQogICAgIlFBIiwgIlJPIiwgIlJVIiwgIlJXIiwgIktOIiwgIkxDIiwgIlZDIiwgIldTIiwgIlNNIiwgIlNUIiwNCiAgICAiU0EiLCAiU04iLCAiUlMiLCAiU0MiLCAiU0wiLCAiU0ciLCAiU0siLCAiU0kiLCAiU0IiLCAiU08iLA0KICAgICJaQSIsICJTUyIsICJFUyIsICJMSyIsICJTRCIsICJTUiIsICJTWiIsICJTRSIsICJDSCIsICJTWSIsDQogICAgIlRXIiwgIlRKIiwgIlRaIiwgIlRIIiwgIlRMIiwgIlRHIiwgIlRPIiwgIlRUIiwgIlROIiwgIlRSIiwNCiAgICAiVE0iLCAiVFYiLCAiVUciLCAiVUEiLCAiQUUiLCAiR0IiLCAiVVMiLCAiVVkiLCAiVVoiLCAiVlUiLA0KICAgICJWQSIsICJWRSIsICJWTiIsICJZRSIsICJaTSIsICJaVyINCiAgKSwNCiAgY2FwaXRhbCA9IGMoDQogICAgIkthYnVsIiwgIlRpcmFuYSIsICJBbGdpZXJzIiwgIkFuZG9ycmEgbGEgVmVsbGEiLCAiTHVhbmRhIiwgIlNhaW50IEpvaG4ncyIsICJCdWVub3MgQWlyZXMiLCAiWWVyZXZhbiIsICJDYW5iZXJyYSIsICJWaWVubmEiLA0KICAgICJCYWt1IiwgIk5hc3NhdSIsICJNYW5hbWEiLCAiRGhha2EiLCAiQnJpZGdldG93biIsICJNaW5zayIsICJCcnVzc2VscyIsICJCZWxtb3BhbiIsICJQb3J0by1Ob3ZvIiwgIlRoaW1waHUiLA0KICAgICJTdWNyZSIsICJTYXJhamV2byIsICJHYWJvcm9uZSIsICJCcmFzw61saWEiLCAiQmFuZGFyIFNlcmkgQmVnYXdhbiIsICJTb2ZpYSIsICJPdWFnYWRvdWdvdSIsICJHaXRlZ2EiLCAiUGhub20gUGVuaCIsICJZYW91bmTDqSIsDQogICAgIk90dGF3YSIsICJQcmFpYSIsICJCYW5ndWkiLCAiTidEamFtZW5hIiwgIlNhbnRpYWdvIiwgIkJlaWppbmciLCAiQm9nb3TDoSIsICJNb3JvbmkiLCAiQnJhenphdmlsbGUiLCAiS2luc2hhc2EiLA0KICAgICJTYW4gSm9zw6kiLCAiWWFtb3Vzc291a3JvIiwgIlphZ3JlYiIsICJIYXZhbmEiLCAiTmljb3NpYSIsICJQcmFndWUiLCAiQ29wZW5oYWdlbiIsICJEamlib3V0aSIsICJSb3NlYXUiLCAiU2FudG8gRG9taW5nbyIsDQogICAgIlF1aXRvIiwgIkNhaXJvIiwgIlNhbiBTYWx2YWRvciIsICJNYWxhYm8iLCAiQXNtYXJhIiwgIlRhbGxpbm4iLCAiQWRkaXMgQWJhYmEiLCAiU3V2YSIsICJIZWxzaW5raSIsICJQYXJpcyIsDQogICAgIkxpYnJldmlsbGUiLCAiQmFuanVsIiwgIlRiaWxpc2kiLCAiQmVybGluIiwgIkFjY3JhIiwgIkF0aGVucyIsICJTdC4gR2VvcmdlJ3MiLCAiR3VhdGVtYWxhIENpdHkiLCAiQ29uYWtyeSIsICJCaXNzYXUiLA0KICAgICJHZW9yZ2V0b3duIiwgIlBvcnQtYXUtUHJpbmNlIiwgIlRlZ3VjaWdhbHBhIiwgIkJ1ZGFwZXN0IiwgIlJleWtqYXZpayIsICJOZXcgRGVsaGkiLCAiSmFrYXJ0YSIsICJUZWhyYW4iLCAiQmFnaGRhZCIsICJEdWJsaW4iLA0KICAgICJKZXJ1c2FsZW0iLCAiUm9tZSIsICJLaW5nc3RvbiIsICJUb2t5byIsICJBbW1hbiIsICJOdXItU3VsdGFuIiwgIk5haXJvYmkiLCAiVGFyYXdhIiwgIlB5b25neWFuZyIsICJTZW91bCIsDQogICAgIkt1d2FpdCBDaXR5IiwgIkJpc2hrZWsiLCAiVmllbnRpYW5lIiwgIlJpZ2EiLCAiQmVpcnV0IiwgIk1hc2VydSIsICJNb25yb3ZpYSIsICJUcmlwb2xpIiwgIlZhZHV6IiwgIlZpbG5pdXMiLA0KICAgICJMdXhlbWJvdXJnIiwgIlNrb3BqZSIsICJBbnRhbmFuYXJpdm8iLCAiTGlsb25nd2UiLCAiS3VhbGEgTHVtcHVyIiwgIk1hbMOpIiwgIkJhbWFrbyIsICJWYWxsZXR0YSIsICJNYWp1cm8iLCAiTm91YWtjaG90dCIsDQogICAgIlBvcnQgTG91aXMiLCAiTWV4aWNvIENpdHkiLCAiUGFsaWtpciIsICJDaGnImWluxIN1IiwgIk1vbmFjbyIsICJVbGFhbmJhYXRhciIsICJQb2Rnb3JpY2EiLCAiUmFiYXQiLCAiTWFwdXRvIiwgIk5heXB5aWRhdyIsDQogICAgIldpbmRob2VrIiwgIllhcmVuIiwgIkthdGhtYW5kdSIsICJBbXN0ZXJkYW0iLCAiV2VsbGluZ3RvbiIsICJNYW5hZ3VhIiwgIk5pYW1leSIsICJBYnVqYSIsICJPc2xvIiwgIk11c2NhdCIsDQogICAgIklzbGFtYWJhZCIsICJOZ2VydWxtdWQiLCAiUmFtYWxsYWgiLCAiUGFuYW1hIENpdHkiLCAiUG9ydCBNb3Jlc2J5IiwgIkFzdW5jacOzbiIsICJMaW1hIiwgIk1hbmlsYSIsICJXYXJzYXciLCAiTGlzYm9uIiwNCiAgICAiRG9oYSIsICJCdWNoYXJlc3QiLCAiTW9zY293IiwgIktpZ2FsaSIsICJCYXNzZXRlcnJlIiwgIkNhc3RyaWVzIiwgIktpbmdzdG93biIsICJBcGlhIiwgIlNhbiBNYXJpbm8iLCAiU8OjbyBUb23DqSIsDQogICAgIlJpeWFkaCIsICJEYWthciIsICJCZWxncmFkZSIsICJWaWN0b3JpYSIsICJGcmVldG93biIsICJTaW5nYXBvcmUiLCAiQnJhdGlzbGF2YSIsICJManVibGphbmEiLCAiSG9uaWFyYSIsICJNb2dhZGlzaHUiLA0KICAgICJQcmV0b3JpYSIsICJKdWJhIiwgIk1hZHJpZCIsICJDb2xvbWJvIiwgIktoYXJ0b3VtIiwgIlBhcmFtYXJpYm8iLCAiTWJhYmFuZSIsICJTdG9ja2hvbG0iLCAiQmVybiIsICJEYW1hc2N1cyIsDQogICAgIlRhaXBlaSIsICJEdXNoYW5iZSIsICJEb2RvbWEiLCAiQmFuZ2tvayIsICJEaWxpIiwgIkxvbcOpIiwgIk51a3UnYWxvZmEiLCAiUG9ydCBvZiBTcGFpbiIsICJUdW5pcyIsICJBbmthcmEiLA0KICAgICJBc2hnYWJhdCIsICJGdW5hZnV0aSIsICJLYW1wYWxhIiwgIkt5aXYiLCAiQWJ1IERoYWJpIiwgIkxvbmRvbiIsICJXYXNoaW5ndG9uLCBELkMuIiwgIk1vbnRldmlkZW8iLCAiVGFzaGtlbnQiLCAiUG9ydCBWaWxhIiwNCiAgICAiVmF0aWNhbiBDaXR5IiwgIkNhcmFjYXMiLCAiSGFub2kiLCAiU2FuYSdhIiwgIkx1c2FrYSIsICJIYXJhcmUiDQogICksDQogIGxhdGl0dWRlID0gYygNCiAgICAzNC41NTUzLCA0MS4zMjc1LCAzNi43NTM4LCA0Mi41MDYzLCAtOC44MzgzLCAxNy4xMTc1LCAtMzQuNjAzNywgNDAuMTc5MiwgLTM1LjI4MDksIDQ4LjIwODIsDQogICAgNDAuNDA5MywgMjUuMDM0MywgMjYuMDY2NywgMjMuODEwMywgMTMuMDk2OSwgNTMuOTA0NSwgNTAuODUwMywgMTcuMjUxMCwgNi40OTY5LCAyNy40NzI4LA0KICAgIC0xOS4wMTk2LCA0My44NTY0LCAtMjQuNjI4MiwgLTE1LjgyNjcsIDQuODg5NSwgNDIuNjk3NywgMTIuMzcxNCwgLTMuMzczMSwgMTEuNTU2NCwgMy44NDgwLA0KICAgIDQ1LjQyMTUsIDE0LjkxNzcsIDQuMzk0NywgMTIuMTM0OCwgLTMzLjQ0ODksIDM5LjkwNDIsIDQuNzExMCwgLTExLjcxNzIsIC00LjI2MzQsIC00LjQ0MTksDQogICAgOS45MjgxLCA2LjgyNzAsIDQ1LjgxNTAsIDIzLjExMzYsIDM1LjE4NTYsIDUwLjA3NTUsIDU1LjY3NjEsIDExLjgyNTEsIDE1LjMwMTcsIDE4LjQ4NjEsDQogICAgLTAuMTgwNywgMzAuMDQ0NCwgMTMuNjkyOSwgMy43NTA0LCAxNS4zMjI5LCA1OS40MzcwLCA5LjAzMjAsIC0xOC4xMjQ4LCA2MC4xNjk5LCA0OC44NTY2LA0KICAgIDAuNDE2MiwgMTMuNDU0OSwgNDEuNzE1MSwgNTIuNTIwMCwgNS42MDM3LCAzNy45ODM4LCAxMi4wNTYxLCAxNC42MzQ5LCA5LjY0MTIsIDExLjg2MzcsDQogICAgNi44MDEzLCAxOC41OTQ0LCAxNC4wNzIzLCA0Ny40OTc5LCA2NC4xNDY2LCAyOC42MTM5LCAtNi4yMDg4LCAzNS42ODkyLCAzMy4zMTUyLCA1My4zNDk4LA0KICAgIDMxLjc2ODMsIDQxLjkwMjgsIDE3Ljk3MTQsIDM1LjY3NjIsIDMxLjk0NTQsIDUxLjE2OTQsIC0xLjI5MjEsIDEuMzM4MiwgMzkuMDM5MiwgMzcuNTY2NSwNCiAgICAyOS4zNzU5LCA0Mi44NzQ2LCAxNy45NzU3LCA1Ni45NDk2LCAzMy44OTM4LCAtMjkuMzE2NywgNi4zMTU2LCAzMi44ODcyLCA0Ny4xNDEwLCA1NC42ODcyLA0KICAgIDQ5LjYxMTYsIDQxLjk5NzMsIC0xOC44NzkyLCAtMTMuOTYyNiwgMy4xMzkwLCA0LjE3NTUsIDEyLjYzOTIsIDM1Ljg5ODksIDcuMDg5NywgMTguMDczNSwNCiAgICAtMjAuMTYwOSwgMTkuNDMyNiwgNi45MTQ3LCA0Ny4wMTA1LCA0My43Mzg0LCA0Ny45MTg2LCA0Mi40MzA0LCAzNC4wMjA5LCAtMjUuOTY1NSwgMTkuNzYzMywNCiAgICAtMjIuNTU5NywgLTAuNTQ3NywgMjcuNzE3MiwgNTIuMzY3NiwgLTQxLjI4NjUsIDEyLjExNTAsIDEzLjUxMjcsIDkuMDc2NSwgNTkuOTEzOSwgMjMuNTg4MCwNCiAgICAzMy42ODQ0LCA3LjUwMDYsIDMxLjk1MjIsIDguOTgyNCwgLTkuNDQzOCwgLTI1LjI2MzcsIC0xMi4wNDY0LCAxNC41OTk1LCA1Mi4yMjk3LCAzOC43MjIzLA0KICAgIDI1LjI4NTQsIDQ0LjQyNjgsIDU1Ljc1NTgsIC0xLjk0MDMsIDE3LjMwMjYsIDEzLjkwOTQsIDEzLjQ4NzcsIC0xMy44MzMzLCA0My45NDI0LCAwLjMzNjQsDQogICAgMjQuNzEzNiwgMTQuNzE2NywgNDQuNzg2NiwgLTQuNjc5NiwgOC40NjU3LCAxLjM1MjEsIDQ4LjE0ODYsIDQ2LjA1NjksIC05LjQ0NTYsIDIuMDQ2OSwNCiAgICAtMjUuNzQ3OSwgNC44NTE3LCA0MC40MTY4LCA2LjkyNzEsIDE1LjUwMDcsIDUuODUyMCwgLTI2LjMwNTQsIDU5LjMyOTMsIDQ2Ljk0ODEsIDMzLjUxMzgsDQogICAgMjUuMDMzMCwgMzguNTU5OCwgLTYuNzkyNCwgMTMuNzU2MywgLTguNTU2OSwgNi4xMjU2LCAtMjEuMTc4OSwgMTAuNjkxOCwgMTEuODc0NSwgMzkuOTMzNCwNCiAgICAzNy45NjAxLCAtOC41MjExLCAwLjM0NzYsIDUwLjQ1MDEsIDI0LjQ1MzksIDUxLjUwNzQsIDM4LjkwNzIsIC0zNC45MDExLCA0MS4yOTk1LCAtMTcuNzMzNCwNCiAgICA0MS45MDI5LCAxMC40ODA2LCAyMS4wMjg1LCAxNS41NTI3LCAtMTUuNDE2NywgLTE3LjgyNTINCiAgKSwNCiAgbG9uZ2l0dWRlID0gYygNCiAgICA2OS4yMDc1LCAxOS44MTg3LCAzLjA1ODgsIDEuNTIxOCwgMTMuMjM0NCwgLTYxLjg0NTYsIC01OC4zODE2LCA0NC40OTkxLCAxNDkuMTMwMCwgMTYuMzczOCwNCiAgICA0OS44NjcxLCAtNzcuMzk2MywgNTAuNTU3NywgOTAuNDEyNSwgLTU5LjYxNjIsIDI3LjU2MTUsIDQuMzUxNywgLTg4Ljc1OTAsIDIuNjI4OSwgODkuNjM5MCwNCiAgICAtNjUuMjYyNywgMTguNDEzMSwgMjUuOTIzMSwgLTQ3LjkyOTIsIDExNC45NDAwLCAyMy4zMjE5LCAtMS41MjQ3LCAyOS44NzM5LCAxMDQuOTI4MiwgMTEuNTAyMSwNCiAgICAtNzUuNjk3MiwgLTIzLjUwODcsIDE4LjU1ODIsIDE1LjA0NDUsIC03MC42NjkzLCAxMTYuNDA3NCwgLTc0LjA3MjEsIDQzLjI1NTEsIDE1LjI2NjIsIDE1LjI2NjMsDQogICAgLTg0LjA5MDcsIC01LjI4OTMsIDE1Ljk4MTksIC04Mi4zNjY2LCAzMy4zODIzLCAxNC40Mzc4LCAxMi41NjgzLCA0My4xNDU2LCAtNjEuMzg3MCwgLTY5LjkzMTIsDQogICAgLTc4LjQ2NzgsIDMxLjIzNTcsIC04OS4yMTgyLCA4Ljc4MzIsIDM4LjkyNTEsIDI0Ljc1MzYsIDM4Ljc0NjksIDE3OC40NDE5LCAyNC45Mzg0LCAyLjM1MjIsDQogICAgOS40NjczLCAtMTYuNTc5MCwgNDQuODI3MSwgMTMuNDA1MCwgLTAuMTg3MCwgMjMuNzI3NSwgLTYxLjc0ODAsIC05MC41MDY5LCAtMTMuNTc4NCwgLTE1LjU5ODksDQogICAgLTU4LjE1NTEsIC03Mi4zMDc0LCAtODcuMjA2OCwgMTkuMDQwMiwgLTIxLjk0MjYsIDc3LjIwOTAsIDEwNi44NDU2LCA1MS4zODkwLCA0NC4zNjYxLCAtNi4yNjAzLA0KICAgIDM1LjIxMzcsIDEyLjQ5NjQsIC03Ni43OTMxLCAxMzkuNjUwMywgMzUuOTI4NCwgNzEuNDcwNCwgMzYuODIxOSwgMTcyLjk3OTAsIDEyNS43NjI1LCAxMjYuOTc4MCwNCiAgICA0Ny45Nzc0LCA3NC41Njk4LCAxMDIuNjAwMCwgMjQuMTA1MiwgMzUuNDk1MywgMjcuNDk3NCwgLTEwLjc5NjksIDEzLjE5MTMsIDkuNTIxNSwgMjUuMjc5NywNCiAgICA2LjEyOTYsIDIxLjQzMTQsIDQ3LjUwNzksIDMzLjc3MzgsIDEwMS42ODY5LCA3My41MDkzLCAtOC4wMDI5LCAxNC41MTQ2LCAxNzEuMTg0NSwgLTE1Ljk1ODIsDQogICAgNTcuNTAxMiwgLTk5LjEzMzIsIDE1OC4xNjExLCAyOC44NDk3LCA3LjQyNDYsIDEwNi45MDU3LCAxOS4yNTk0LCAtNi44NDk4LCAzMi41NzMyLCA5Ni4xNTYxLA0KICAgIDE3LjA4MzIsIDE2Ni45MzE1LCA4NS4zMjQwLCA0LjkwNDEsIDE3NC43NjMzLCAtODYuMjM2MiwgMi4xMjU0LCA3LjQ5NTEsIDEwLjc1MjIsIDU4LjQwNTksDQogICAgNzMuMDQ3OSwgMTM0LjQ4OTMsIDM1LjIzMzIsIC03OS41MTk5LCAxNDcuMTgwMywgLTU3LjU3NTksIC03Ny4wNDI4LCAxMjAuOTg0MiwgMjEuMDEyMiwgLTkuMTM5MywNCiAgICA1MS41MzEwLCAyNi4xMDI1LCAzNy42MTczLCAtMS45NTM2LCAtNjIuNzE3NywgLTYwLjk3ODksIC02MS4yMjQ4LCAtMTcxLjc2NjcsIDEyLjQ1NzgsIDYuNzI3MywNCiAgICA0Ni43MTYwLCAtMTcuNDQ2NywgMjAuNDQ4OSwgNTUuNDU0MCwgLTEzLjIzMTcsIDEwMy44MTk4LCAxNy4xMDc3LCAxNC41MDU4LCAxNTkuOTcyOSwgNDUuMzE4MiwNCiAgICAyOC4xODc5LCAzMS41ODI1LCAtMy43MDM4LCA3OS44NjEyLCAzMi41NTk5LCAtNTUuMjAzOCwgMzEuMTM2NywgMTguMDY4NiwgNy40NDc0LCAzNi4yNzY1LA0KICAgIDEyMS41NjU0LCA2OC43ODcwLCAzNS43NTE2LCAxMDAuNTAxOCwgMTI1LjU2MDMsIDEuMjMxMywgLTE3NS4xOTgyLCAtNjEuNTA4OSwgMTAuMTgxNSwgMzIuODU5NywNCiAgICA1OC4zMjYwLCAxNzkuMTk0MiwgMzIuNTgyNSwgMzAuNTIzNCwgNTQuMzc3MywgLTAuMTI3OCwgLTc3LjAzNjksIC01Ni4xNjQ1LCA2OS4yNDAxLCAxNjguMzI3MywNCiAgICAxMi40NTM0LCAtNjYuODc5MiwgMTA1Ljg1NDIsIDQ0LjIwNzAsIDI4LjI4NzEsIDMxLjA1MjINCiAgKSwNCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQopDQoNCiMgVmlldyB0aGUgZGF0YWZyYW1lDQpoZWFkKGNhcGl0YWxfY29vcmRzLCAxMCkNCg0KIyBDaGVjayB0aGF0IFRhaXdhbiBpcyBpbmNsdWRlZA0KY2FwaXRhbF9jb29yZHNbY2FwaXRhbF9jb29yZHMkaXNvMiA9PSAiVFciLCBdDQoNCiMgT3B0aW9uYWw6IFNvcnQgYnkgSVNPMiBjb2RlDQpjYXBpdGFsX2Nvb3JkcyA8LSBjYXBpdGFsX2Nvb3Jkc1tvcmRlcihjYXBpdGFsX2Nvb3JkcyRpc28yKSwgXQ0KDQojIFByaW50IHN1bW1hcnkNCmNhdCgiVG90YWwgY291bnRyaWVzOiIsIG5yb3coY2FwaXRhbF9jb29yZHMpLCAiXG4iKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShtYXBzKQ0KDQojIENyZWF0ZSBnZ3Bsb3QNCndvcmxkX21hcCA8LSBtYXBfZGF0YSgid29ybGQiKQ0KDQpwIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB3b3JsZF9tYXAsIA0KICAgICAgICAgICAgICAgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwNCiAgICAgICAgICAgICAgIGZpbGwgPSAibGlnaHRncmF5IiwgDQogICAgICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsIA0KICAgICAgICAgICAgICAgbGluZXdpZHRoID0gMC4yKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGNhcGl0YWxfY29vcmRzLA0KICAgICAgICAgICAgIGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUsIA0KICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKCJDYXBpdGFsOiAiLCBjYXBpdGFsLCAiXG4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvdW50cnk6ICIsIGlzbzIsICJcbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29vcmRpbmF0ZXM6ICIsIHJvdW5kKGxhdGl0dWRlLCAyKSwgIiwgIiwgcm91bmQobG9uZ2l0dWRlLCAyKSkpLA0KICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIA0KICAgICAgICAgICAgIHNpemUgPSAyLCANCiAgICAgICAgICAgICBhbHBoYSA9IDAuNykgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHRpdGxlID0gIldvcmxkIENhcGl0YWwgQ2l0aWVzIC0gSW50ZXJhY3RpdmUiLA0KICAgICAgIHggPSAiTG9uZ2l0dWRlIiwNCiAgICAgICB5ID0gIkxhdGl0dWRlIikNCg0KIyBDb252ZXJ0IHRvIGludGVyYWN0aXZlIHBsb3RseQ0KZ2dwbG90bHkocCwgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lDQogIGxheW91dCgNCiAgICBob3ZlcmxhYmVsID0gbGlzdChiZ2NvbG9yID0gIndoaXRlIiwgZm9udCA9IGxpc3Qoc2l6ZSA9IDEyKSkNCiAgKQ0KDQpgYGANCg0KDQoNCiMjIG5vdyBpbmZlciBsb25nIGFuZCBsYXRzIGZyb20gZWl0aGVyIHRoZSBzdWNjZXNmdWwgbWF0Y2hlcyBvciBpZiB0aGVyZSBpcyBub25lLi4ubGV0J3MganVzdCB1c2UgdGhlIGNhcGl0YWwgbG9jYXRpb24uLi4NCg0KDQpgYGB7cn0NCiAgbGlicmFyeShiaWdycXVlcnkpDQogIGxpYnJhcnkoREJJKQ0KICANCiAgIyBTZXQgeW91ciBwcm9qZWN0IElEDQogIHByb2plY3RfaWQgPC0gInBhdGJpcyINCiAgDQogICMgT3B0aW9uIDE6IERpcmVjdCB0YWJsZSBkb3dubG9hZA0KICAjIFNwZWNpZnkgZGF0YXNldCBhbmQgdGFibGUgbmFtZQ0KICBkYXRhc2V0IDwtICJpbmdsb2JlIg0KICB0YWJsZSA8LSAicGVyc29uX2FkZHJlc3NfZXh0cmFjdCINCiAgDQoNCiAgIyBVcGxvYWQgLSB0aGlzIGNyZWF0ZXMgb3IgcmVwbGFjZXMgdGhlIHRhYmxlDQpicV90YWJsZV91cGxvYWQoDQogIHggPSBicV90YWJsZShwcm9qZWN0X2lkLCBkYXRhc2V0LCAibGF0X2xvbmdzX21hcGJveCIpLA0KICB2YWx1ZXMgPSBsYXRfbG9uZ3NfbWFwYm94LA0KICAjZmllbGRzID0gZGYsICAjIE9wdGlvbmFsOiBzcGVjaWZ5IHNjaGVtYQ0KICB3cml0ZV9kaXNwb3NpdGlvbiA9ICJXUklURV9UUlVOQ0FURSIgICMgUmVwbGFjZXMgZXhpc3RpbmcgdGFibGUNCikNCg0KDQoNCmNhcGl0YWxfY29vcmRzPWNhcGl0YWxfY29vcmRzICU+JSBtdXRhdGUocGVyc29uX2N0cnlfY29kZT1pc28yKSAlPiUgc2VsZWN0KHBlcnNvbl9jdHJ5X2NvZGUsbGF0aXR1ZGUsbG9uZ2l0dWRlKQ0KYnFfdGFibGVfdXBsb2FkKA0KICB4ID0gYnFfdGFibGUocHJvamVjdF9pZCwgZGF0YXNldCwgImNhcGl0YWxfY29vcmRzIiksDQogIHZhbHVlcyA9IGNhcGl0YWxfY29vcmRzLA0KICAjZmllbGRzID0gZGYsICAjIE9wdGlvbmFsOiBzcGVjaWZ5IHNjaGVtYQ0KICB3cml0ZV9kaXNwb3NpdGlvbiA9ICJXUklURV9UUlVOQ0FURSIgICMgUmVwbGFjZXMgZXhpc3RpbmcgdGFibGUNCikNCg0KICANCg0KYGBgDQoNCg0KDQoNCg0KDQpgYGB7cn0NCg0KDQoNCmFudGk9ZGYgJT4lIGFudGlfam9pbihsYXRfbG9uZ3NfbWFwYm94KSAlPiUgc2VsZWN0KHBlcnNvbl9pZCxwZXJzb25fY3RyeV9jb2RlKSANCmFudGk9YW50aSAlPiUgZmlsdGVyKCFpcy5uYShwZXJzb25fY3RyeV9jb2RlKSAmIGdzdWIoIlxccysiLCAiIiwgcGVyc29uX2N0cnlfY29kZSkhPSIiKQ0KDQoNCg0KDQoNCmxhdF9sb25nPWxhdF9sb25nc19tYXBib3ggJT4lIHNlbGVjdChsb25naXR1ZGUsbGF0aXR1ZGUscGVyc29uX2N0cnlfY29kZSkgJT4lIGJpbmRfcm93cyhjYXBpdGFsX2Nvb3JkcykgJT4lIGdyb3VwX2J5KHBlcnNvbl9jdHJ5X2NvZGUpICU+JSANCiAgbXV0YXRlKHJyPXJ1bmlmKDE6bigpKSkgJT4lIGFycmFuZ2UocGVyc29uX2N0cnlfY29kZSwgcnIpICU+JSBtdXRhdGUobm49MTpuKCkpICU+JSBmaWx0ZXIobm48MykgJT4lICAjIExldCdzIHJlc3RyaWN0IHRvIDUgcGxhY2VzIHBlciBjb3VudHJ5Li4uDQogIHNlbGVjdChsb25naXR1ZGUsbGF0aXR1ZGUscGVyc29uX2N0cnlfY29kZSkNCg0KDQphbnRpPWFudGkgJT4lIGxlZnRfam9pbihsYXRfbG9uZyxieT0icGVyc29uX2N0cnlfY29kZSIpICU+JQ0KICBncm91cF9ieShwZXJzb25faWQpICU+JSBtdXRhdGUocnI9cnVuaWYoMTpuKCkpKSAlPiUgYXJyYW5nZShwZXJzb25faWQsIHJyKSAlPiUgbXV0YXRlKG5uPTE6bigpKSAlPiUgZmlsdGVyKG5uPT0xKQ0KDQoNCmFudGk9YW50aSAlPiUgc2VsZWN0KC1ubiwtcnIpDQoNCmRmbmV3PWFudGkgJT4lIGJpbmRfcm93cyhsYXRfbG9uZ3NfbWFwYm94KSAlPiUgc2VsZWN0KHBlcnNvbl9pZCwgcGVyc29uX2N0cnlfY29kZSxsb25naXR1ZGUsbGF0aXR1ZGUpDQoNCg0KDQp3cml0ZV9wYXJxdWV0KGRmbmV3LHBhc3RlMChsb2NhbGJpZywiXFxkYXRhXFxkZm5ldy5wYXJxdWV0IikpDQoNCg0KDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShhcnJvdykNCmRmbmV3PXJlYWRfcGFycXVldChwYXN0ZTAobG9jYWxiaWcsIlxcZGF0YVxcZGZuZXcucGFycXVldCIpKQ0KDQoNCiNkZl9zYW1wbGUgPC0gZGZuZXcgJT4lIHNsaWNlX3NhbXBsZShuID0gMTAwMDApDQoNCg0KDQpsaWJyYXJ5KGRhdGEudGFibGUpDQpkZm5ldyA8LSBhcy5kYXRhLnRhYmxlKGRmbmV3KQ0KZHRfc2FtcGxlIDwtIGRmbmV3W3NhbXBsZSguTiwgMTAwMDAwKV0NCg0KIyBXaXRoIGNsdXN0ZXJpbmcgKG11Y2ggYmV0dGVyIGZvciAxMDBrIHBvaW50cyEpDQpsZWFmbGV0KGR0X3NhbXBsZSkgJT4lDQogIGFkZFRpbGVzKCkgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoDQogICAgbG5nID0gfmxvbmdpdHVkZSwNCiAgICBsYXQgPSB+bGF0aXR1ZGUsDQogICAgcmFkaXVzID0gMywNCiAgICBmaWxsT3BhY2l0eSA9IDAuNSwNCiAgICBzdHJva2UgPSBGQUxTRSwNCiAgICBjbHVzdGVyT3B0aW9ucyA9IG1hcmtlckNsdXN0ZXJPcHRpb25zKCkNCiAgKQ0KDQoNCg0KDQoNCmBgYA0KDQoNCg0KDQpgYGB7cixldmFsPUZBTFNFfQ0KYnFfdGFibGVfdXBsb2FkKA0KICB4ID0gYnFfdGFibGUocHJvamVjdF9pZCwgZGF0YXNldCwgImRmbmV3IiksDQogIHZhbHVlcyA9IGRmbmV3LA0KICAjZmllbGRzID0gZGYsICAjIE9wdGlvbmFsOiBzcGVjaWZ5IHNjaGVtYQ0KICB3cml0ZV9kaXNwb3NpdGlvbiA9ICJXUklURV9UUlVOQ0FURSIgICMgUmVwbGFjZXMgZXhpc3RpbmcgdGFibGUNCikNCmBgYA0KICANCg0K